当用户想切换登录账号,或者想退出登录状态时,这时候就需要注销已登录的账号。现在我们来为网站添加注销登录的功能,这个功能 Django 也已经为我们提供,我们只需做一点简单配置。
注销登录
注销登录的视图为 logout,我们简单修改一下 index.html 的代码,添加一个注销登录的按钮:
templates/index.html
{% if user.is_authenticated %}
<p>你已登录,欢迎你:<a href="#">{{ user.username }}</a></p>
<button class="btn btn-default"><a href="{% url 'logout' %}">注销登录</a></button>
{% else %}
<p>你还没有登录,请
<button class="btn btn-default"><a href="{% url 'login' %}">登录</a></button>
或者
<button class="btn btn-default"><a href="{% url 'users:register' %}">注册</a></button>
</p>
{% endif %}
如果你已经登陆,就会看到一个注销登录的按钮,点击该按钮就会跳转到注销登录已成功地页面。再一次访问首页,你将看到登录、注册按钮,说明你已经成功注销登录状态了。
页面跳转
我们之前在登录、注册和注销的过程中发现,登录成功后会跳转到一个 404 页面,注册成功后返回的是首页,而注销登录后跳转到了 Admin 后台的注销成功页面。对于一个网站来说,比较好的用户体验是登录、注册和注销后跳转回用户之前访问的页面。否则用户在你的网站东跳转西跳转好不容易找到了想看的内容,结果他已登录给他跳转回了首页,这会使用户非常愤怒(我在有些网站就遇到过)。接下来我们看看如何让登录、注册和注销后跳转回用户之前访问的页面。
登录和注销后返回当前页面
在登录和注销的视图函数中,Django 已经为我们处理了跳转回用户之前访问页面的流程。其实现的原理是,在登录和注销的流程中,始终传递一个 next 参数记录用户之前访问页面的 URL。因此,我们需要做的就是在用户访问登录或者注销的页面时,在 URL 中传递一个 next 参数给视图函数,具体做法如下:
templates/index.html
<button class="btn btn-default">
<a href="{% url 'logout' %}?next={{ request.path }}">注销登录</a>
</button>
<button class="btn btn-default">
<a href="{% url 'login' %}?next={{ request.path }}">登录</a>
</button>
可以看到,我们在登录和注销的 URL 后加了 next 参数,其值为 {{ request.path }}。request.path 是用户当前访问页面的 URL。在 URL 中传递参数的方法就是在要传递的参数前加一个 ?然后写上传递的参数名和参数值,用等号链接。关于在 URL 中传递参数具体请 HTTP 的相关协议。
为了在整个登录流程中记录 next 的值,还需要在登录表单中增加一个表单控件,用于传递 next 值。
registration/login.html
<form class="form" action="{% url 'login' %}" method="post">
...
<button type="submit" class="btn btn-primary btn-block">登录</button>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
即在表单中增加了一个隐藏的 input 控件,其值为 {{ next }},即之前通过 URL 参数传递给登录视图函数的,然后登录视图函数又将该值传递给了 login.html 模板。这样在整个登录流程中,始终有一个记录着用户在登录前页面 URL 的变量 next 在视图和模板间来回传递,知道用户登录成功后再跳转回 next 记录的页面 URL。
现在你可以点击登录和注销的按钮来走一遍登录和注销流程,发现页面跳转已经符合我们的需求了。不过还由一点点小瑕疵,就是如果用户不是通过点击登录和注销按钮,而是直接在页面输入 URL 来访问相关页面话,那这个 next 就没有值,从而无法向之前那样跳转回用户之前访问的页面。比如用户想登录,他直接在浏览器的地址栏输入 /users/login/,由于在 URL 中没有传递 next,所以就无法记录用户登录前的页面 URL,那在登录成功后就无法将他带回登录前的页面了。当然这种情况是极为罕见的,很少有用户会记住你网站的 URL 地址,但如果真有这样的用户,我们就把他跳转回首页吧,因为没有任何办法记录他之前访问的页面。要想把用户跳转回首页,可以在 settings 中做如下设置:
LOGOUT_REDIRECT_URL = '/'
LOGIN_REDIRECT_URL = '/'
这样,整个登录和注销流程就形成了一个闭环。如果用户通过点击登录或者注销按钮登录和注销的话,在登录或者注销成功后就会被带回登录或者注销前的页面,否则将他带回网站首页。
注册后返回当前页面
类似的,我们也希望用户注册后返回注册前页面。不过由于注册视图函数是我们自己写的,之前的处理方式是用户注册成功后将其带回网站首页,因此需要修改一下注册视图函数:
users/views.py
def register(request):
# 从 get 或者 post 请求中获取 next 参数值
# get 请求中,next 通过 url 传递,即 /?next=value
# post 请求中,next 通过表单传递,即 <input type="hidden" name="next" value="{{ next }}"/>
redirect_to = request.POST.get('next', request.GET.get('next', ''))
# 只有当请求为 POST 时,才表示用户提交了注册信息
if request.method == 'POST':
# request.POST 是一个类字典数据结构,记录了用户提交的注册信息
# 这里提交的就是用户名(username)、密码(password)、确认密码、邮箱(email)
# 用这些数据实例化一个用户注册表单
form = RegisterForm(request.POST)
# 验证数据的合法性
if form.is_valid():
# 如果提交数据合法,调用表单的 save 方法将用户数据保存到数据库
form.save()
if redirect_to:
return redirect(redirect_to)
else:
return redirect('/')
else:
# 请求不是 POST,表明用户正在访问注册页面,展示一个空的注册表单给用户
form = RegisterForm()
# 渲染模板
# 如果用户正在访问注册页面,则渲染的是一个空的注册表单
# 如果用户通过表单提交注册信息,但是数据验证不合法,则渲染的是一个带有错误信息的表单
# 将记录用户注册前页面的 redirect_to 传给模板,以维持 next 参数在整个注册流程中的传递
return render(request, 'users/register.html', context={'form': form, 'next': redirect_to})
逻辑非常简答,就是首先尝试从用户的 GET 或者 POST 请求中获取 next 参数值,即在注册成功后需要跳转的 URL,如果有值,注册成功后跳转到该 URL,否则跳转回首页。同是不要忘记将该值传给模板,以维持 next 参数在整个注册流程中的传递。
接下来修改模板,和登录模板的设置是一样的:
registration/login.html
<button class="btn btn-default">
<a href="{% url 'users:register' %}?next={{ request.path }}">注册</a>
</button>
templates/users/register.html
<form class="form" action="{% url 'users:register' %}" method="post">
...
<button type="submit" class="btn btn-primary btn-block">注册</button>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
注意:在注册视图函数中,对 next 的任意值我们都进行了跳转,这可能导致一些安全问题。正确的做法应该是在跳转前,对需要跳转的 URL 做安全性检查。不过这里只作为一个示例,在实际项目中请仔细考虑可能的安全后果,以及添加必要的安全性检查代码。
OK,如此修改以后,用户的登录、注册和注销流程的用户体验可以形成一个比较良好闭环了。接下来就来实现修改密码的功能。
总结
本教程的示例项目代码位于 GitHub:Django Auth Example。
如果遇到问题,请通过下面的方式寻求帮助。
- 在下方评论区留言。
- 在 Pythonzhcn 社区的新手问答版块 发布帖子。
更多 Django 相关教程,请访问我的个人博客:追梦人物的博客。
-- EOF --
传递给login的next参数如果改为mynext
它还会正常跳转吗?
next参数是contrib.auth.login内置的关键字?
的确 next是一个关键字,
看来下
django.contrib.auth
源代码,里面设置了REDIRECT_FIELD_NAME = 'next'
在
LoginView
里面可以看出,next
参数 最终被传递到get_redirect_url
函数。django3.0.5无法在index页面实现登录、注销和注册的跳转,博主能帮我解答一下嘛?
这个没办法跳转到登录
但是
<button class="btn btn-default"><a href="{% url 'login' %}?next={{ request.path }}">登录</a></button>
这个没有办法实现跳转登录页面但是
<div class="unit-2-3"><a href="{% url 'login' %}?next={{ request.path }}">登录</a></div>
这个可以实现跳转。IE浏览器的问题,换成Google就好了,其他朋友也引以为戒吧,我硬生生看了一下午, - -!!
提一个问题:
在 index.html 中对登录和注册的地址分别添加了 request.path
从 index 界面分别进入登录界面与注册界面进行测试,在登录界面登录成功时的确会跳转回 index 界面,但是在注册界面注册成功却不会跳转回 index 界面。
我查看了一下两个界面提交 post 请求是的 request.POST ,发现登录界面的 request.POST 里的 next 是 '/index',但是注册界面的确为空,不知道是什么原因。
现在又可以了,不知道什么原因。
长时间不操作会自动注销吗
博主,我在登陆后设置了几个只有登录后才能进入的界面,这些界面里都有注销这个按键,但是点了注销之后就404了,没有办法点击注销之后跳转到主界面吗?
文中已经给出解决方案:
即设置你的next值
如果需要注册完成之后自动登录的话,可以views.register函数中,在if form.is_valid():的判断中,先将form.save()赋值给新的变量user,也就是user = form.save(),接着导入函数from django.contrib.auth import login as auth_login,然后auth_login(request, user)就可以了
谢谢!
谢谢,很细心的小姐姐!
谢谢博主
博主你少些一个在register.html的隐藏标签,否则在views.py函数写的获取next的参数没什么作用了,只能在登录界面跳转到注册界面的时候在url中看到next参数,而真正从注册界面跳转到用户之前登录的界面是不能实现的(即跳转不到redirect_to),因为你没有在register.html中设置隐藏标签,所以也就获取不到next参数。所以注册成功后一定会跳转到index.html界面。另外感谢博主!教会我这么多东西
不是少了隐藏标签,是没加index.html注册button的next获取
谢谢博主的文章,很强大!
但是前端的button标签和a标签好像有点冲突,重叠在一起的button标签内部嵌入a标签,点击按钮没反应,a标签的跳转作用就没有了,只用a标签不用button标签页面可以正常跳转。不知道是不是这个问题。
我也遇到了这个问题,应该是按钮不兼容firefox(我猜你用的也是Firefox,因为我测试了别的浏览器,都好使)。另外真心感谢博主,一直在看博主的文章,认真学习中!谢谢!
对,我是用firefox浏览器,我后来也测试了其他浏览器,没什么问题,应该就是浏览器兼容的原因
博主您好,十分感谢您的博客,对我帮助很大,提个无关代码逻辑的意见:
这里的
registration/login.html
应该是registration/index.html
,:)最后再次表示感谢,找了很多用户管理的实现都没找到满意的,直到看到您的博客,很受用,辛苦了
您好,我这里有个问题,用户成功注册后,网站上应该出现一个提示说用户成功注册了啊,但是您这么直接redirect到了之前的界面,用户根本不知道发生了什么。。
嗯,我同意你的观点。我觉得可以使用 django.contrib.message 框架生成提示消息。
对的,可以在教程了补充一下,后面有很多都有一样的问题。谢谢您
请问下。。如何使用 django.contrib.message 呢。
比如说我想在 用户注册成功的时候,有个提示框,但是我根据文档来使用这个模块,都没有成功。
能不能写个示例出来呢。。谢谢了
我想文档已经说得非常清楚了,不用重复造轮子吧。我来写可能还没文档写的好。
博主你好,我发现在我的环境ubuntu16.04 python2.7 firefox浏览器下示例中给出的
这可能是浏览器兼容性问题。
博主这里有一个登录逻辑问题:如果是从登录界面的注册进入注册页面,注册完成后跳回的是注册页面,而我们需要跳回的是点击登录时的页面
博主这里有一个登录逻辑问题:如果是从登录界面的注册进入注册页面,注册完成后跳回的是登录页面,而我们需要跳回的是点击登录时的页面
(上面留言写错了)
嗯,相信明白了原理后自己定制一下跳转流程也是很简单的。我这里仅给出一个示例。
这里还是有个小小漏洞,就是我注册之后应该返回的是登录后渲染的首页,而不应该是未登录渲染的界面。这个该怎么处理呢?貌似is_authenticatied只对login管用
不会的,只要你登录成功后显示的页面就是 authenticated 过的页面了。
我也遇到了同样的问题,注册成功后返回未登录 的主页
注册后是需要再次登录才行的,没有自动登录机制。
老师,我想请问一下在注销用户的时候,我在urls.py中添加了url(r'^logout', views.log_out, name='log_out'),并且在views.py中添加对应的log_out函数调用django自带的logout()函数,才能实现您这里说的注销。仅仅更改index.html中的url 'logout'会提示找不到合适的正则匹配,请问是不是我哪里方法不对?
额,找到问题了,因为添加这个语句url(r'^', include('django.contrib.auth.urls'))的时候把users也带上了,所以地址不对。打扰啦学到很多,老师辛苦了~