设置用户帐户
在本节中,我们将建立一个用户注册和授权系统,以便用户可以注册账户、登录和注销。我们将创建一个新的应用程序,其中包含与用户相关的所有功能。我们将尽可能多地使用 Django 附带的默认用户身份验证系统。我们还将稍微修改一下主题模型,以便每个主题都属于某个用户。
账户应用程序
首先,我们将使用 startapp 命令创建一个名为 accounts 的新应用程序:
(ll_env)learning_log$ python manage.py startapp accounts
(ll_env)learning_log$ ls
❶ accounts db.sqlite3 learning_logs ll_env ll_project manage.p
y
(ll_env)learning_log$ ls accounts
❷ __init__.py admin.py apps.py migrations models.py tests.py v
iews.py
默认的身份验证系统是围绕用户账户的概念构建的,因此使用 accounts 这个名称可以更容易地与默认系统集成。这里显示的 startapp 命令创建了一个名为 accounts ❶ 的新目录,其结构与 learning_logs app ❷ 相同。
在 settings.py 中添加账户
我们需要将新应用程序添加到 settings.py 中的 INSTALLED_APPS
,就像这样:
--snip--
INSTALLED_APPS = [
# My apps
'learning_logs',
'accounts',
# Default django apps.
--snip--
]
--snip--
现在 Django 将在整个项目中包含帐户应用程序。
包括帐户中的 URL
接下来,我们需要修改根 urls.py,使其包含我们为账户应用程序编写的 URL:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
path('', include('learning_logs.urls')),
]
我们添加一行内容,以包含 accounts
中的 urls.py 文件。这一行将匹配任何以 accounts 开头的 URL,如 http://localhost:8000/accounts/login/ 。
登录页面
我们首先要实现一个登录页面。我们将使用 Django 提供的默认登录视图,因此此应用程序的 URL 模式看起来有点不同。在 ll_project/accounts/ 目录下新建一个 urls.py 文件,并添加以下内容:
"""Defines URL patterns for accounts."""
from django.urls import path, include
app_name = 'accounts'
urlpatterns = [
# Include default auth urls.
path('', include('django.contrib.auth.urls')),
]
我们导入 path 函数,然后导入 include 函数,这样我们就能包含 Django 定义的一些默认身份验证 URL。这些默认 URL 包括命名的 URL 模式,如 "登录 "和 "注销"。我们将变量 app_name 设置为 "账户",这样 Django 就能将这些 URL 与属于其他应用程序的 URL 区分开来。即使是 Django 提供的默认 URL,如果包含在 accounts 应用程序的 urls.py 文件中,也可以通过 accounts 命名空间访问。
登录页面的模式与 URL http://localhost:8000/accounts/login/ 匹配。当 Django 读取该 URL 时,accounts 会告诉 Django 在 accounts/urls.py 中查找,而 login 则会告诉 Django 将请求发送到 Django 的默认登录视图。
登录模板
当用户请求登录页面时,Django 会使用默认视图函数,但我们仍需要为页面提供模板。默认身份验证视图会在名为 registration 的文件夹中查找模板,因此我们需要创建该文件夹。在 ll_project/accounts/ 目录中,创建一个名为 templates 的目录;在该目录中,创建另一个名为 registration 的目录。下面是 login.xhtml 模板,应保存在 ll_project/accounts/templates/registration 目录中:
{% extends 'learning_logs/base.xhtml' %}
{% block content %}
❶ {% if form.errors %}
<p>Your username and password didn't match. Please try ag
ain.</p>
{% endif %}
❷ <form action="{% url 'accounts:login' %}" method='post'>
{% csrf_token %}
❸ {{ form.as_div }}
❹ <button name="submit">Log in</button>
</form>
{% endblock content %}
该模板扩展了 base.xhtml,以确保登录页面具有与网站其他部分相同的外观和感觉。请注意,一个应用程序中的模板可以继承自另一个应用程序中的模板。
如果设置了表单的 errors 属性,我们就会显示一条错误信息❶,报告用户名和密码组合与数据库中存储的内容不匹配。
我们希望登录视图处理表单,因此将 action 参数设置为登录页面的 URL ❷。登录视图会向模板发送一个表单对象,然后由我们来显示表单❸ 并添加一个提交按钮❹。
LOGIN_REDIRECT_URL 设置
用户登录成功后,Django 需要知道将该用户发送到哪里。我们在设置文件中对此进行控制。
在 settings.py 的末尾添加以下代码:
--snip--
# My settings.
LOGIN_REDIRECT_URL = 'learning_logs:index'
settings.py 中包含了所有默认设置,因此在添加新设置的部分做标记很有帮助。我们要添加的第一个新设置是 LOGIN_REDIRECT_URL,它会告诉 Django 登录成功后重定向到哪个 URL。
链接到登录页面
让我们将登录链接添加到 base.xhtml,这样它就会出现在每个页面上。我们不希望在用户已经登录的情况下显示该链接,因此将其嵌套在 {% if %}
标记中:
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
-
<a href="{% url 'learning_logs:topics' %}">Topics</a> -
❶ {% if user.is_authenticated %}
❷ Hello, {{ user.username }}.
{% else %}
❸ <a href="{% url 'accounts:login' %}">Log in</a>
{% endif %}
</p>
{% block content %}{% endblock content %}
在 Django 的身份验证系统中,每个模板都有一个可用的用户对象,该对象总是设置有 is_authenticated 属性:如果用户已登录,则该属性为 True;如果用户未登录,则该属性为 False。通过该属性,您可以向已通过身份验证的用户显示一条信息,而向未通过身份验证的用户显示另一条信息。
在这里,我们向当前已登录的用户❶ 显示问候语。已通过身份验证的用户有一个额外的用户名属性设置,我们用它来个性化问候语,并提醒用户他们已登录❷。对于尚未通过身份验证的用户,我们会显示一个登录页面的链接❸。
使用登录页面
我们已经设置了一个用户账户,现在登录看看页面是否正常。访问 http://localhost:8000/admin/ 。如果仍以管理员身份登录,请在页眉处查找注销链接并点击。
注销后,访问 http://localhost:8000/accounts/login/ 。您应该会看到与图 19-4 所示类似的登录页面。输入之前设置的用户名和密码,就会回到主页。主页的页眉应显示带有用户名的个性化问候语。

注销
现在,我们需要为用户提供一种注销方式。注销请求应以 POST 请求的形式提交,因此我们将在 base.xhtml 中添加一个小的注销表单。当用户点击注销按钮时,他们将进入一个页面,确认他们已经注销。
在 base.xhtml 中添加注销表单
我们将在 base.xhtml 中添加注销表单,这样每个页面都能看到它。我们将把它包含在另一个 if 块中,这样只有已经登录的用户才能看到它:
--snip--
{% block content %}{% endblock content %}
{% if user.is_authenticated %}
❶ <hr />
❷ <form action="{% url 'accounts:logout' %}" method='post'>
{% csrf_token %}
<button name='submit'>Log out</button>
</form>
{% endif %}
注销的默认 URL 模式是 "accounts/logout/"。不过,该请求必须以 POST 请求的形式发送,否则攻击者可以轻易地强制注销请求。为了让注销请求使用 POST 方式,我们定义了一个简单的表单。
我们将表单放在页面底部,水平规则元素(<hr />)❶ 的下方。这是一种简便的方法,可以使注销按钮始终位于页面上任何其他内容下方的一致位置。表单本身的操作参数是注销 URL,请求方法是 "post" ❷。Django 中的每个表单都需要包含 {% csrf_token %}
,即使像这个表单这样简单的表单也不例外。除了提交按钮,该表单是空的。
注册页面
接下来,我们将创建一个页面,以便新用户注册。我们将使用 Django 的默认 UserCreationForm
,但要编写自己的视图函数和模板。
注册 URL
以下代码提供了注册页面的 URL 模式,应将其放在 accounts/urls.py 中:
"""Defines URL patterns for accounts."""
from django.urls import path, include
from . import views
app_name = accounts
urlpatterns = [
# Include default auth urls.
path('', include('django.contrib.auth.urls')),
# Registration page.
path('register/', views.register, name='register'),
]
我们从 accounts 中导入了视图模块,因为我们要为注册页面编写自己的视图,所以需要视图模块。注册页面的模式与 URL http://localhost:8000/accounts/register/ 匹配,并将请求发送到我们将要编写的 register() 函数。
register() 视图函数
register()
视图函数需要在首次请求注册页面时显示空白注册表单,然后在提交完成的注册表单后对其进行处理。注册成功后,该函数还需要登录新用户。在 accounts/views.py 中添加以下代码:
from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
def register(request):
"""Register a new user."""
if request.method != 'POST':
# Display blank registration form.
form = UserCreationForm() # 1
else:
# Process completed form.
form = UserCreationForm(data=request.POST) # 2
if form.is_valid(): # 3
new_user = form.save() # 4
# Log the user in and then redirect to home page.
login(request, new_user) # 5
return redirect('learning_logs:index') # 6
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'registration/register.xhtml', context)
我们导入 render() 和 redirect() 函数,然后导入 login() 函数,以便在用户注册信息正确的情况下登录用户。我们还导入了默认的 UserCreationForm。在 register() 函数中,我们会检查是否正在响应 POST 请求。如果不是,我们就创建一个没有初始数据❶ 的 UserCreationForm 实例。
如果我们正在响应 POST 请求,我们就会根据提交的数据❷ 创建一个 UserCreationForm 实例。我们会检查数据是否有效❸--在这种情况下,用户名是否有适当的字符,密码是否匹配,以及用户在提交数据时是否有任何恶意行为。
如果提交的数据有效,我们就调用表单的 save() 方法将用户名和密码的哈希值保存到数据库❹。save() 方法会返回新创建的用户对象,我们将其赋值给 new_user。保存用户信息后,我们会使用请求和 new_user 对象❺调用 login() 函数登录用户,从而为新用户创建一个有效的会话。最后,我们会将用户重定向到主页❻,在主页头部的个性化问候语会告诉用户注册成功。
函数结束时,我们将渲染页面,页面要么是空白表单,要么是已提交但无效的表单。
注册模板
现在为注册页面创建一个模板,它将与登录页面类似。确保将其保存在与 login.xhtml 相同的目录下:
{% extends "learning_logs/base.xhtml" %}
{% block content %}
<form action="{% url 'accounts:register' %}" method='post'>
{% csrf_token %}
{{ form.as_div }}
<button name="submit">Register</button>
</form>
{% endblock content %}
这看起来就像我们编写的其他基于表单的模板一样。我们再次使用 as_div 方法,这样 Django 就会适当地显示表单中的所有字段,包括表单未正确填写时的任何错误信息。
链接到注册页面
接下来,我们将添加代码,向当前未登录的用户显示注册页面链接:
--snip--
{% if user.is_authenticated %}
Hello, {{ user.username }}.
{% else %}
<a href="{% url 'accounts:register' %}">Register</a> -
<a href="{% url 'accounts:login' %}">Log in</a>
{% endif %}
--snip--
现在,已登录的用户会看到个性化问候语和注销按钮。未登录的用户会看到一个注册链接和一个登录链接。用不同的用户名创建几个用户账户,试用注册页面。
在下一节中,我们将限制某些页面,使其只对注册用户开放,并确保每个主题都属于某个特定用户。
我们建立的注册系统允许任何人创建任意数量的学习日志账户。有些系统要求用户通过发送确认电子邮件来确认身份,而用户必须回复该电子邮件。与我们这里使用的简单系统相比,这样做可以减少系统生成的垃圾账户。不过,在学习构建应用程序时,完全可以使用我们正在使用的这种简单的用户注册系统进行练习。 |