登录

用户已经能够在我们的网站注册了,注册就是为了登录,接下来我们为用户提供登录功能。和注册不同的是,Django 已经为我们写好了登录功能的全部代码,我们不必像之前处理注册流程那样费劲了。只需几分钟的简单配置,就可为用户提供登录功能。接下来就来看看如何使用内置的登录功能。

引入内置的 URL 模型

Django 内置的登录、修改密码、找回密码等视图函数对应的 URL 模式位于 django.contrib.auth.urls.py 中,首先在工程的 urls.py 文件里包含这些 URL 模式。打开 django_auth_example/ 目录下的 urls.py 文件,将 django.contrib.auth.urls.py 包含进来:

django_auth_example/urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users/', include('users.urls')),
    # 将 auth 应用中的 urls 模块包含进来
    url(r'^users/', include('django.contrib.auth.urls')),
]

这将包含以下的 URL 模式:

^users/login/$ [name='login']
^users/logout/$ [name='logout']
^users/password_change/$ [name='password_change']
^users/password_change/done/$ [name='password_change_done']
^users/password_reset/$ [name='password_reset']
^users/password_reset/done/$ [name='password_reset_done']
^users/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ [name='password_reset_confirm']
^users/reset/done/$ [name='password_reset_complete']

设置模板路径

默认的登录视图函数渲染的是 registration/login.html 模板,因此需要在 templates/ 目录下新建一个 registration 文件夹,再在 registration/ 目录下新建 login.html 模板文件。此时目录结构为:

django_auth_example/
    manage.py
    django_auth_example/
        __init__.py
        settings.py
        urls.py
        wsgi.py
    templates/
        users/
            register.html
        registration/
            login.html

编写登录模板

登录模板的代码和注册模板的代码十分类似:

registration/login.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>登录</title>
    <link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
    <style>
        .errorlist {
            color: red;
        }
    </style>
</head>
<body>
<div class="flex-center">
    <div class="container">
        <div class="flex-center">
            <div class="unit-1-2 unit-1-on-mobile">
                <h3>登录</h3>
                <form class="form" action="{% url 'login' %}" method="post">
                    {% csrf_token %}
                    {{ form.non_field_errors }}
                    {% for field in form %}
                        {{ field.label_tag }}
                        {{ field }}
                        {{ field.errors }}
                        {% if field.help_text %}
                            <p class="help text-small text-muted">{{ field.help_text|safe }}</p>
                        {% endif %}
                    {% endfor %}
                    <button type="submit" class="btn btn-primary btn-block">登录</button>
                </form>
                <div class="flex-left top-gap text-small">
                    <div class="unit-2-3"><span>没有账号?<a href="{% url 'users:register' %}">立即注册</a></span></div>
                    <div class="unit-1-3 flex-right"><span><a href="reset_password.html">忘记密码?</a></span></div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

为了登录页面的美观我引入了 mobi.css 提供样式支持,其它代码请忽略,我们只关注表单部分的代码。

<form class="form" action="{% url 'login' %}" method="post">
  {% csrf_token %}
  {{ form.non_field_errors }}
  {% for field in form %}
    {{ field.label_tag }}
    {{ field }}
    {{ field.errors }}
    {% if field.help_text %}
        <p class="help text-small text-muted">{{ field.help_text|safe }}</p>
    {% endif %}
  {% endfor %}
  <button type="submit" class="btn btn-primary btn-block">登录</button>
</form>

循环表单字段、渲染控件、渲染帮助信息等在注册表单部分已经讲过,登录表单中只引入了一个新的东西:{{ form.non_field_errors }},这显示的同样是表单错误,但是显示的表单错误是和具体的某个表单字段无关的。相对 {{ field.errors }},这个则显示的是具体某个字段的错误。比如对于字段 username,如果用户输入的 username 不符合要求,比如太长了或者太短了,表单会在 username 下方渲染这个错误。但有些表单错误不和任何具体的字段相关,比如用户输入的用户名和密码无法通过验证,这可能是用户输入的用户名不存在,也可能是用户输入的密码错误,因此这个错误信息将通过 {{ form.non_field_errors }} 渲染。

注意:你可能觉得用户名不存在错误和 username 字段有关,密码错误和 password 字段有关。但是在现代的用户认证系统中,我们不为用户提供这么详细的信息,只是笼统地告知用户名不存在或者密码错误。这能提高一些用户账户的安全性。

此外登录表单的 action 属性的值是 {% url 'login' %},即 auth 应用下的 login 视图函数对应的 URL,用户提交的表单数据将提交给这个 URL,Django 调用 login 视图函数来处理。

不要忘了加 {% csrf_token %} 模板标签。

现在打开开发服务器,在浏览器输入 http://127.0.0.1:8000/users/login/,你将看到一个用户登陆表单。

用户登陆表单

故意使用一个不存在的账户登录,或者故意输错密码,你将看到表单渲染的非字段相关的错误。

登录错误信息

如果用户登录成功,你会发现跳转到了 http://127.0.0.1:8000/accounts/profile/ 页面。由于我们没有写任何视图函数处理这个 URL,所以看到一个 404 错误。不过没有关系,我们目前只关注用户是否已经登录。

如何在模板中判断用户是否已经登录

在模板中判断用户是否已经登录非常简单,使用 {% if user.is_authenticated %} 条件判断即可。借此机会,我们来处理一下网站首页。

在 user/views.py 写一个首页视图函数:

user/views.py 

def index(request):
    return render(request, 'index.html')

为这个视图函数配置 URL 模式,在 django_auth_example/urls.py 进行配置:

from django.conf.urls import url, include
from django.contrib import admin

from users import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 别忘记在顶部引入 include 函数
    url(r'^users/', include('users.urls')),
    url(r'^users/', include('django.contrib.auth.urls')),
    # 别忘记在顶部引入 views 模块
    url(r'^$', views.index, name='index')
]

注意:直接在项目的 urls.py 中配置 URL 是不推荐的,应该在应用的 urls.py 下进行配置,然后在项目的 urls.py 中通过 include 函数包含。不过这里作为示例情况特殊,所以姑且这样做。

由于视图渲染了 index.html 文件,因此在 templates/ 目录下建一个 index.html 模板文件(注意我们没有把它放在 users/ 下,也没放在 registration/ 下)。然后写上下面的代码:

templates/index.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>首页</title>
    <link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
</head>
<body>
<div class="flex-center">
    <div class="container">
        <div>
            <h1 class="logo"><a href="{% url 'index' %}">Django Auth Example</a></h1>
            {% if user.is_authenticated %}
                <p>你已登录,欢迎你:<a href="#">{{ user.username }}</a></p>
            {% 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 %}
        </div>
    </div>
</div>
</body>
</html>

为了页面的美观,我引入了 mobi.css 提供样式支持。其它代码请忽略,重点只关注用户登录验证部分:

{% if user.is_authenticated %}
    <p>你已登录,欢迎你:<a href="#">{{ user.username }}</a></p>
{% 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 %}

user.is_authenticated 当用户已经登录时返回 True,否则返回 False。所以已登录的用户将看到欢迎页面,否则将看到登录注册按钮。

你也许奇怪我们在 index 视图中并没有传递 user 模板变量给 index.html,为什么可以在模板中引用 user 呢?这是因为 Django 的 auth 应用为我们设置了模板常量,所以在任何模板中都可以引用 {{ user }}。此外,我们之前提过的 django.contrib.auth.middleware.AuthenticationMiddleware 为所有的请求 request 绑定了一个 user 属性。所以在模板中引用 {{ user }} 和 {{ request.user }} 是等价。

OK 了!不过目前为止,如果你已经登录过了,想要看看未登录的效果会变得比较困难,因为我们还无法注销登录。下面就来给网站添加注销登录的功能吧!

总结

本教程的示例项目代码位于 GitHub:Django Auth Example

如果遇到问题,请通过下面的方式寻求帮助。

更多 Django 相关教程,请访问我的个人博客:追梦人物的博客

-- EOF --


47 条评论 / 24 人参与
金陵第一名剑凯文

 登录超级用户之后,修改或者添加 user,跳转出错,怎么解决呢?


pumpkinpies

您好 请问如何设置未登录 直接跳转至 登录页呢 ? 现在的逻辑是要有一个按钮?


追梦人物 [博主] pumpkinpies

如果是类视图函数,添加 LoginRequiredMixin,视图函数则添加 login_required 装饰器,具体直接搜索关键词就可以看到相关用法了。


用户6231586962

您好,这个在后台添加的账号不能登录是为什么。

后台添加一个账号,在到登录页面登录会显示账号密码错误。


追梦人物 [博主] 用户6231586962

请使用 createuser 或者 createsuperuser 命令。直接添加会导致密码为明文,django是不允许的。


King-Ye 追梦人物 [博主]

事实上,Django是允许从后台添加的。只是因为他的后台没有从UserAdmin继承过来,所以密码是明文。参照 https://abersheeran.com/articles/Django-Admin-Extend-0/ 里自定义用户模型那。


ylllllllll__

请问要怎么写视图跳转至别的网页


zhazhalaila ylllllllll__

默认的跳转链接是'/accounts/profile/',如果在登陆后想要跳转其它链接,可以在setting.py中加入一行代码

LOGIN_REDIRECT_URL = '/' 

上面的代码就可以跳转到首页了,如果想跳转到其它界面更改一下即可

参考链接


用户6231586962

按照这个教程来,似乎只有在注册页面注册的用户才能登录,在admin后台添加的用户是无法登录的?


追梦人物 [博主] 用户6231586962

都是可以登录的呀


用户6231586962 追梦人物 [博主]

貌似是无法登录的,尝试过很多次了,只有在注册页面注册的账号才能登录,后台添加的无法登录。

或者是我的这个出现了什么问题?


King-Ye 用户6231586962

可能是你的后台管理不是从UserAdmin继承过来的。你这样试试

from django.contrib.auth.admin import UserAdmin

@admin.register(User)
class User_Admin(UserAdmin):
pass

William Wong

追梦人物 [博主] William Wong

没事的,这也是我发的,我也支持大家随意地转载。


JingWu

博主你好,我想问一下点击注册页面的‘已有账号登录’,是跳转到http://127.0.0.1:8000/users/register/login.html而不是http://127.0.0.1:8000/users/login,这样导致点击后不会出现登录页面,怎样解决这个问题呢?


追梦人物 [博主] JingWu

修改你的 已有账号登录 的跳转链接,最好用 url 标签来生成跳转链接。


饭特惜Fantasy

我在首页点击登录后,出现错误Exception Type:TypeErrorException Value:login() missing 1 required positional argument: 'user',请问有人知道原因和解决方法吗?谢谢!


追梦人物 [博主] 饭特惜Fantasy

怀疑是版本问题?或者你改动了某个地方的代码逻辑?


Carl Cai 饭特惜Fantasy

我也遇到了,后来发现 urls 那里写了 views.login,而这个 login 是 django 自带的,然后在 views 导入了,我自己写登录函数的是其他名字的,所以报了这个错。改成自己的登录函数就可以了。 


yiwozai

博主你好,有个小问题。 Django翻译的 “注意他们都是大区分大小写的” 有什么方法快速、自定义修改吗? 谢谢


追梦人物 [博主] yiwozai

请准确地描述问题。


chuanleixu

jjj


黑色的忧虑2011

博主你好,请问登录页成功后是怎样跳转到index页面的?这块login页面和index页面没有什么联系啊?

本来跳转到http://127.0.0.1:8000/accounts/profile/页面,完了咱们自己弄了个index,求解怎么就登录成功跳转到index页面的???


sixto 黑色的忧虑2011

在后面一章。


Talk3r

请问博主,"由于视图渲染了 index.html 文件,因此在 templates/ 目录下建一个 index.html 模板文件(注意我们没有把它放在 users/ 下,也没放在 registration/ 下)",这个是为什么?如果把所有模板文件放在templates文件夹下行不行呢?真心请教....☺


追梦人物 [博主] Talk3r

可以的,只要设置好模板路径,能够让 django 找到就可以了。


jintao2luo

博主,url(r'^users/', include('django.contrib.auth.urls'))这个地方不是很懂


追梦人物 [博主] jintao2luo

include 就是把 django.contrib.auth.urls 应用下的 url 包含到项目 url 配置文件下,这样你才能访问这些 url。你打开这个文件就可以看到里面的 url 定义了。


大江东流奔腾不息
文章写得相当不错!

皮皮欧呢
如果自己写一个注册登录注销以及更改密码是不是很麻烦?主要用的啥

追梦人物 [博主] 皮皮欧呢
是的,很麻烦,所以用 django 内置的 auth 应用

猪突鲁直5982407159
博主啥时候出下django allauth的教程

追梦人物 [博主] 猪突鲁直5982407159
暂时无计划,先自己研究研究文档吧。

猪突鲁直5982407159 追梦人物 [博主]
恩 我刚也测了新浪微博登陆 有些小问题还没解决;
另外想问下博主是怎么编辑博文的呢? 是直接往django的管理界面的数据库里粘贴markdown文本还是什么?

追梦人物 [博主] 猪突鲁直5982407159
我现在本地写好 markdown,发布时复制粘贴就可以了。

猪突鲁直5982407159 追梦人物 [博主]
谢谢

Ehco
请问博主,这个登录功能不需要我们自己写view和form,
那我怎么自定义form渲染出来的input的class呢?
还有我需要在view函数里加一些自定义的context,我改如何实现呢?

追梦人物 [博主] Ehco
这些都可以通过继承 django 内部的 form 和 view 拓展,不过需要你熟悉它们的源码,这里我只是浅尝辄止,没有深入讨论。

Ehco 追梦人物 [博主]
是的,我将form和view全部自己写了,实现了功能
谢谢!

宝子要加油
请问博主,如果只做到这一部分,还没开始设置下一步的next, 是不是无法成功跳转到index页面,而是会跳转到/accounts/profile/.

追梦人物 [博主] 宝子要加油
是的,往下读,之后的教程会解决这个问题。

小杰好啊
想请问博主,为什么注册的用户登录时总是说密码不对,登录进去,但是超级用户就没有问题呢?

追梦人物 [博主] 小杰好啊
在后台看看是否已经注册成功了。

小杰好啊 追梦人物 [博主]
在后台看见了有这个用户,不过查到资料说需要把is_active变成TRUE,不过看博主用的表单我就不会操作了

小杰好啊 追梦人物 [博主]
啊,我发现问题了,是在那个login.html的页面中{% url 。。。%}后面加了?{。。。}之后成功登陆之后会又跳转到.../user/login/?next=/user/login/
#大概是这个网址,记不清了。这个网址就会提醒你密码错误

小杰好啊 小杰好啊
我自己加错了哎。。。

mihelloO
哈,刚好看到,顶!