《Django By Example》第四章 上 中文 翻译 (个人学习,渣翻)

全文链接

第一章 创建一个blog应用
第二章 使用高级特性来增强你的blog
第三章 扩展你的blog应用
第四章上 创建一个社交网站
第四章下 创建一个社交网站
第五章 在你的网站中分享内容
第六章 跟踪用户动作
第七章 建立一个在线商店
第八章 管理付款和订单
第九章上 扩展你的商店
第九章下 扩展你的商店
第十章上 创建一个在线学习平台
第十章下 创建一个在线学习平台
第十一章 缓存内容
第十二章 构建一个API

书籍出处:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé

2017年1月4日初稿发布

2017年5月5日第一次精校完成(感谢大牛 @kukoo 的精校!)

(译者注:祝大家新年快乐,这次带来《Django By Example》第四章的翻译,这章非常的实用,就这样)

第四章

创建一个社交网站

在上一章中,你学习了如何创建站点地图(sitemaps)和feeds,你还为你的blog应用创建了一个搜索引擎。在本章中,你将开发一个社交应用。你会为用户创建一些功能,例如:登录,登出,编辑,以及重置他们的密码。你会学习如何为你的用户创建一个定制的profile,你还会为你的站点添加社交认证。

本章将会覆盖一下几点:

  • 使用认证(authentication)框架
  • 创建用户注册视图(views)
  • 通过一个定制的profile模型(model)扩展User模型(model)
  • 使用python-social-auth添加社交认证

让我们开始创建我们的新项目吧。

创建一个社交网站项目

我们要创建一个社交应用允许用户分享他们在网上找到的图片。我们需要为这个项目构建以下元素:

  • 一个用来给用户注册,登录,编辑他们的profile,以及改变或重置密码的认证(authentication)系统
  • 一个允许用户来关注其他人的关注系统(这里原文是follow,‘跟随’,感觉用‘关注’更加适合点)
  • 为用户从其他任何网站分享过来的图片进行展示和打上书签
  • 每个用户都有一个活动流允许他们看到所关注的人上传的内容

本章主要讲述第一点。

开始你的社交网站项目

打开终端使用如下命令行为你的项目创建一个虚拟环境并且激活它:

mkdir evn
virtualenv evn/bookmarks
source env/bookmarks/bin/activate

shell提示将会展示你激活的虚拟环境,如下所示:

(bookmarks)laptop:~ zenx$

通过以下命令在你的虚拟环境中安装Django:

pip install Django==1.8.6

运行以下命令来创建一个新项目:

django-admin startproject bookmarks

在创建好一个初始的项目结构以后,使用以下命令进入你的项目目录并且创建一个新的应用命名为account:

cd bookmarks/
django-admin startapp account

请记住在你的项目中激活一个新应用需要在settings.py文件中的INSTALLED_APPS设置中添加它。将新应用的名字添加在INSTALLED_APPS列中的所有已安装应用的最前面(这里我一定要申明下!account一定要放在最前面!!!!不然后面会有各类问题!!!!),如下所示:

INSTALLED_APPS = (
    'account',
    # ...
)

运行下一条命令为INSTALLED_APPS中默认包含的应用模型(models)同步到数据库中:

python manage.py migrate

我们将要使用认证(authentication)框架来构建一个认证系统到我们的项目中。

使用Django认证(authentication)框架

Django拥有一个内置的认证(authentication)框架用来操作用户认证(authentication),会话(sessions),权限(permissions)以及用户组。这个认证(authentication)系统包含了一些普通用户的操作视图(views),例如:登录,登出,修改密码以及重置密码。

这个认证(authentication)框架位于django.contrib.auth,被其他Django的contrib包调用。请记住你在第一章 创建一个Blog应用中使用过这个认证(authentication)框架并用来为你的blog应用创建了一个超级用户来使用管理站点。

当你使用startproject命令创建一个新的Django项目,认证(authentication)框架已经在你的项目设置中默认包含。它是由django.contrib.auth应用和你的项目设置中的MIDDLEWARE_CLASSES中的两个中间件类组成,如下:

  • AuthenticationMiddleware:使用会话(sessions)将用户和请求(requests)进行关联
  • SessionMiddleware:通过请求(requests)操作当前会话(sessions)

中间件就是一个在请求和响应阶段带有全局执行方法的类。你会在本书中的很多场景中使用到中间件。你将会在第十三章 Going Live中学习如何创建一个定制的中间件(译者注:啥时候能翻译到啊)。

这个认证(authentication)系统还包含了以下模型(models):

  • User:一个包含了基础字段的用户模型(model);这个模型(model)的主要字段有:username, password, email, first_name, last_name, is_active。
  • Group:一个组模型(model)用来分类用户
  • Permission:执行特定操作的标识

这个框架还包含默认的认证(authentication)视图(views)和表单(forms),我们之后会用到。

创建一个log-in视图(view)

我们将要开始使用Django认证(authentication)框架来允许用户登录我们的网站。我们的视图(view)需要执行以下操作来登录用户:

<ol>
<li>通过提交的表单(form)获取username和password
<li>通过存储在数据库中的数据对用户进行认证
<li>检查用户是否可用
<li>登录用户到网站中并且开始一个认证(authentication)会话(session)
</ol>

首先,我们要创建一个登录表单(form)。在你的account应用目录下创建一个新的forms.py文件,添加如下代码:

from django import forms
        
class LoginForm(forms.Form):        
    username = forms.CharField()        
    password = forms.CharField(widget=forms.PasswordInput)

这个表单(form)被用来通过数据库认证用户。请注意,我们使用PasswordInput控件来渲染HTMLinput元素,包含type="password"属性。编辑你的account应用中的views.py文件,添加如下代码:

from django.http import HttpResponse    
from django.shortcuts import render    
from django.contrib.auth import authenticate, login    
from .forms import LoginForm 

   
def user_login(request):        
    if request.method == 'POST':            
        form = LoginForm(request.POST)            
        if form.is_valid():                
            cd = form.cleaned_data                
            user = authenticate(username=cd['username'],
                                password=cd['password'])                
            if user is not None:                    
                if user.is_active:                        
                    login(request, user)                        
                    return HttpResponse('Authenticated successfully')
                else:                        
                    return HttpResponse('Disabled account')
            else:            
                return HttpResponse('Invalid login')        
    else:            
        form = LoginForm()        
    return render(request, 'account/login.html', {'form': form})

以上就是我们在视图(view)中所作的基本登录操作:当user_login被一个GET请求(request)调用,我们实例化一个新的登录表单(form)并通过form = LoginForm()在模板(template)中展示它。当用户通过POST方法提交表单(form)时,我们执行以下操作:

1、通过使用form = LoginForm(request.POST)使用提交的数据实例化表单(form)
2、检查这个表单是否有效。如果无效,我们在模板(template)中展示表单错误信息(举个例子,比如用户没有填写其中一个字段就进行提交)
3、如果提交的数据是有效的,我们使用authenticate()方法通过数据库对这个用户进行认证(authentication)。这个方法带入一个username和一个password,如果这个用户成功的进行了认证则返回一个用户对象,否则是None。如果用户没有被认证通过,我们返回一个HttpResponse展示一条消息。
4、如果这个用户认证(authentication)成功,我们使用is_active属性来检查用户是否可用。这是一个Django的User模型(model)属性。如果这个用户不可用,我们返回一个HttpResponse展示信息。
5、如果用户可用,我们登录这个用户到网站中。我们通过调用login()方法集合用户到会话(session)中然后返回一条成功消息。

请注意authenticationlogin中的不同点:authenticate()检查用户认证信息,如果用户是正确的则返回一个用户对象;login()将用户设置到当前的会话(session)中。

现在,你需要为这个视图(view)创建一个URL模式。在你的account应用目录下创建一个新的urls.py文件,添加如下代码:

from django.conf.urls import url    
from . import views    

urlpatterns = [        
    # post views        
    url(r'^login/$', views.user_login, name='login'),    
]

编辑位于你的bookmarks项目目录下的urls.py文件,将account应用下的URL模式包含进去:

from django.conf.urls import include, url    
from django.contrib import admin
    
urlpatterns = [        
    url(r'^admin/', include(admin.site.urls)),
    url(r'^account/',include('account.urls')),    
]

这个登录视图(view)现在已经可以通过URL进行访问。现在是时候为这个视图(view)创建一个模板。因为之前你没有这个项目的任何模板,你可以开始创建一个主模板(template)能够被登录模板(template)继承使用。在account应用目录中创建以下文件和结构:

    templates/
        account/
            login.html
        base.html

编辑base.html文件添加如下代码:

{% load staticfiles %}    
<!DOCTYPE html>    
<html>    
    <head>      
        <title>{% block title %}{% endblock %}</title>      
        <link href="{% static "css/base.css" %}" rel="stylesheet">    
    </head>    
    <body>      
        <div id="header">        
            <span class="logo">Bookmarks</span>      
        </div>      
        <div id="content">        
            {% block content %}        
            {% endblock %}      
        </div>    
    </body>    
</html>

以上将是这个网站的基础模板(template)。就像我们在上一章项目中做过的一样,我们在这个主模板(template)中包含了CSS样式。你可以在本章的示例代码中找到这些静态文件。复制示例代码中的account应用下的static/目录到你的项目中的相同位置,这样你就可以使用这些静态文件了。

基础模板(template)定义了一个title和一个content区块可以让被继承的子模板(template)填充内容。

让我们为我们的登录表单(form)创建模板(template)。打开account/login.html模板(template)添加如下代码:

{% extends "base.html" %}
{% block title %}Log-in{% endblock %}
{% block content %}
    <h1>Log-in</h1>
    <p>Please, use the following form to log-in:</p>
    <form action="." method="post">
        {{ form.as_p }}
        {% csrf_token %}
        <p><input type="submit" value="Log-in"></p>
    </form>
{% endblock %}

这个模板(template)包含了视图(view)中实例化的表单(form)。因为我们的表单(form)将会通过POST方式进行提交,所以我们包含了{% csrf_token %}模板(template)标签(tag)用来通过CSRF保护。你已经在第二章 使用高级特性扩展你的博客应用中学习过CSRF保护。

目前还没有用户在你的数据库中。你首先需要创建一个超级用户用来登录管理站点来管理其他的用户。打开命令行执行python manage.py createsuperuser。填写username, e-mail以及password。之后通过命令python manage.py runserver运行开发环境,然后在你的浏览器中打开 http://127.0.0.1:8000/admin/ 。使用你刚才创建的username和passowrd来进入管理站点。你会看到Django管理站点包含Django认证(authentication)框架中的UserGroup模型(models)。如下所示:

使用管理站点创建一个新的用户然后打开 http://127.0.0.1:8000/account/login/ 。你会看到被渲染过的模板(template),包含一个登录表单(form):

现在,只填写一个字段保持另外一个字段为空进行表单(from)提交。在这例子中,你会看到这个表单(form)是无效的并且显示了一个错误信息:

如果你输入了一个不存在的用户或者一个错误的密码,你会得到一个Invalid login信息。

如果你输入了有效的认证信息,你会得到一个Authenticated successfully消息:

使用Django认证(authentication)视图(views)

Django在认证(authentication)框架中包含了一些开箱即用的表单(forms)和视图(views)。你之前创建的登录视图(view)是一个非常好的练习用来理解Django中的用户认证(authentication)过程。无论如何,你可以在大部分的案例中使用默认的Django认证(authentication)视图(views)。

Django提供以下视图(views)来处理认证(authentication):

  • login:操作表单(form)中的登录然后登录一个用户
  • logout:登出一个用户
  • logout_then_login:登出一个用户然后重定向这个用户到登录页面

Django提供以下视图(views)来操作密码修改:

  • password_change:操作一个表单(form)来修改用户密码
  • password_change_done:当用户成功修改他的密码后提供一个成功提示页面

Django还包含了以下视图(views)允许用户重置他们的密码:

  • password_reset:允许用户重置他的密码。它会生成一条带有一个token的一次性使用链接然后发送到用户的邮箱中。
  • password_reset_done:告知用户已经发送了一封可以用来重置密码的邮件到他的邮箱中。
  • password_reset_complete:当用户重置完成他的密码后提供一个成功提示页面。

当你创建一个带有用户账号的网站时,以上的视图(views)可以帮你节省很多时间。你可以覆盖这些视图(views)使用的默认值,例如需要渲染的模板位置或者视图(view)需要使用到的表单(form)。

你可以通过访问 https://docs.
djangoproject.com/en/1.8/topics/auth/default/#module-django.contrib.auth.views 获取更多内置的认证(authentication)视图(views)信息。

登录和登出视图(views)

编辑你的account应用下的urls.py文件,如下所示:

from django.conf.urls import url
from . import views
urlpatterns = [
    # previous login view
    # url(r'^login/$', views.user_login, name='login'),
    # login / logout urls
    url(r'^login/$',
        'django.contrib.auth.views.login',
        name='login'),
    url(r'^logout/$',
        'django.contrib.auth.views.logout',
        name='logout'),
    url(r'^logout-then-login/$',
        'django.contrib.auth.views.logout_then_login',
        name='logout_then_login'),
]

我们将之前创建的user_login视图(view)URL模式进行注释,然后使用Django认证(authentication)框架提供的login视图(view)。

【译者注】如果使用Django 1.10以上版本,urls.py 需要改写为以下方式(参见源书提供的源代码):

from django.conf.urls import url
from django.contrib.auth.views import login
# With django 1.10 I need to pass the callable instead of 
# url(r'^login/$', 'django.contrib.auth.views.login', name='login')

from django.contrib.auth.views import logout
from django.contrib.auth.views import logout_then_login
from django.contrib.auth.views import password_change
from django.contrib.auth.views import password_change_done
from django.contrib.auth.views import password_reset
from django.contrib.auth.views import password_reset_done
from django.contrib.auth.views import password_reset_confirm
from django.contrib.auth.views import password_reset_complete
from . import views

urlpatterns = [
    # post views
    # url(r'^login/$', views.user_login, name='login'),
    
    # login logout
    url(r'^login/$', login, name='login'),
    url(r'^logout/$', logout, name='logout'),
    url(r'^logout-then-login/$', logout_then_login, name='logout_then_login'),
    # change password
    url(r'^password-change/$', password_change, name='password_change'),
    url(r'^password-change/done/$', password_change_done, name='password_change_done'),
    # reset password
    ## restore password urls
    url(r'^password-reset/$',
        password_reset,
        name='password_reset'),
    url(r'^password-reset/done/$',
        password_reset_done,
        name='password_reset_done'),
    url(r'^password-reset/confirm/(?P<uidb64>[-\w]+)/(?P<token>[-\w]+)/$',
        password_reset_confirm,
        name='password_reset_confirm'),
    url(r'^password-reset/complete/$',
        password_reset_complete,
        name='password_reset_complete'),
    ]

在你的account应用中的template目录下创建一个新的目录命名为registration。这个路径是Django认证(authentication)视图(view)期望你的认证(authentication)模块(template)默认的存放路径。在这个新目录中创建一个新的文件,命名为login.html,然后添加如下代码:

{% extends "base.html" %}
{% block title %}Log-in{% endblock %}
{% block content %}
  <h1>Log-in</h1>
  {% if form.errors %}
    <p>
      Your username and password didn't match.
      Please try again.
    </p>
  {% else %}
    <p>Please, use the following form to log-in:</p>
  {% endif %}
  <div class="login-form">
    <form action="{% url 'login' %}" method="post">
      {{ form.as_p }}
      {% csrf_token %}
      <input type="hidden" name="next" value="{{ next }}" />
      <p><input type="submit" value="Log-in"></p>
    </form>
  </div>
 {% endblock %}

这个登录模板(template)和我们之前创建的基本类似。Django默认使用位于django.contrib.auth.forms中的AuthenticationForm。这个表单(form)会尝试对用户进行认证,如果登录不成功就会抛出一个验证错误。在这个例子中,如果用户提供了错误的认证信息,我们可以在模板(template)中使用{% if form.errors %}来找到错误。请注意,我们添加了一个隐藏的HTML<input>元素来提交叫做next的变量值。当你在请求(request)中传递一个next参数(举个例子:http://127.0.0.1:8000/account/login/?next=/account/),这个变量是登录视图(view)首个设置的参数。

next参数必须是一个URL。当这个参数被给予的时候,Django登录视图(view)将会在用户登录完成后重定向到给予的URL。

现在,在registrtion模板(template)目录下创建一个logged_out.html模板(template)添加如下代码:

{% extends "base.html" %}
{% block title %}Logged out{% endblock %}
{% block content %}
  <h1>Logged out</h1>
  <p>You have been successfully logged out. You can <a href="{% url "login" %}">log-in again</a>.</p>
{% endblock %}

Django会在用户登出的时候展示这个模板(template)。

在添加了URL模式以及登录和登出视图(view)的模板之后,你的网站已经准备好让用户使用Django认证(authentication)视图进行登录。

请注意,在我们的地址配置中所包含的logout_then_login视图(view)不需要任何模板(template),因为它执行了一个重定向到登录视图(view)。

现在,我们要创建一个新的视图(view)给用户,在他或她登录他们的账号后来显示一个dashboard。打开你的account应用中的views.py文件,添加以下代码:

from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
    return render(request,
                 'account/dashboard.html',
                 {'section': 'dashboard'})

我们使用认证(authentication)框架的login_required装饰器(decorator)装饰我们的视图(view)。login_required装饰器(decorator)会检查当前用户是否通过认证,如果用户通过认证,它会执行装饰的视图(view),如果用户没有通过认证,它会把用户重定向到带有一个名为next的GET参数的登录URL,该GET参数保存的变量为用户当前尝试访问的页面URL。通过这些动作,登录视图(view)会将登录成功的用户重定向到用户登录之前尝试访问过的URL。请记住我们在登录模板(template)中的登录表单(form)中添加的隐藏<input>就是为了这个目的。

我们还定义了一个section变量。我们会使用该变量来跟踪用户在站点中正在查看的页面。多个视图(views)可能会对应相同的section。这是一个简单的方法用来定义每个视图(view)对应的section。

现在,你需要创建一个给dashboard视图(view)使用的模板(template)。在templates/account/目录下创建一个新文件命名为dashboard.html。添加如下代码:

{% extends "base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
  <h1>Dashboard</h1>
  <p>Welcome to your dashboard.</p>
{% endblock %}

之后,为这个视图(view)在account应用中的urls.py文件中添加如下URL模式:

urlpatterns = [
    # ...
    url(r'^$', views.dashboard, name='dashboard'),
]

编辑你的项目的settings.py文件,添加如下代码:

from django.core.urlresolvers import reverse_lazy
LOGIN_REDIRECT_URL = reverse_lazy('dashboard')
LOGIN_URL = reverse_lazy('login')
LOGOUT_URL = reverse_lazy('logout')

这些设置的意义:

  • LOGIN_REDIRECT_URL:告诉Django用户登录成功后如果contrib.auth.views.login视图(view)没有获取到next参数将会默认重定向到哪个URL。
  • LOGIN_URL:重定向用户登录的URL(例如:使用login_required装饰器(decorator))。
  • LOGOUT_URL:重定向用户登出的URL。

我们使用reverse_lazy()来通过它们的名字动态构建URL。reverse_lazy()方法就像reverse()所做的一样reverses URLs,但是你可以通过使用这种方式在你项目的URL配置被读取之前进行reverse URLs。

让我们总结下目前为止我们都做了哪些工作:

  • 你在项目中添加了Django内置的认证(authentication)登录和登出视图(views)。
  • 你为每一个视图(view)创建了定制的模板(templates),并且定义了一个简单的视图(view)让用户登录后进行重定向。
  • 最后,你配置了你的Django设置使用默认的URLs。

现在,我们要给我们的主模板(template)添加登录和登出链接将所有的一切都连接起来。

为了做到这点,我们必须确定无论用户是已登录状态还是没有登录的时候,都会显示适当的链接。通过认证(authentication)中间件当前的用户被设置在HTTP请求(request)对象中。你可以通过使用request.user访问用户信息。你会发现一个用户对象在请求(request)中,即便这个用户并没有认证通过。一个未认证的用户在请求(request)中被设置成一个AnonymousUser的实例。一个最好的方法来检查当前的用户是否通过认证是通过调用request.user.is_authenticated()

编辑你的base.html文件修改ID为header<div>元素,如下所示:

<div id="header">
<span class="logo">Bookmarks</span>
{% if request.user.is_authenticated %}
     <ul class="menu">
      <li {% if section == "dashboard" %}class="selected"{% endif %}>
        <a href="{% url "dashboard" %}">My dashboard</a>
      </li>
      <li {% if section == "images" %}class="selected"{% endif %}>
        <a href="#">Images</a>
      </li>
      <li {% if section == "people" %}class="selected"{% endif %}>
        <a href="#">People</a>
       </li>
      </ul>
     {% endif %}
     <span class="user">
       {% if request.user.is_authenticated %}
         Hello {{ request.user.first_name }},
         <a href="{% url "logout" %}">Logout</a>
       {% else %}
         <a href="{% url "login" %}">Log-in</a>
       {% endif %}
    </span>
</div>

就像你所看到的,我们只给通过认证(authentication)的用户显示站点菜单。我们还检查当前的section并通过使用CSS来给对应的<li>组件添加一个selected的class属性来使当前section在菜单中进行高亮显示。如果用户已经通过认证(authentication),我们还显示用户的名字(first_name)和一个登出的链接,否则,就是一个登出链接。

现在,在浏览器中打开 http://127.0.0.1:8000/account/login/ 。你会看到登录页面。输入可用的用户名和密码然后点击Log-in按钮。你会看到如下图所示:

你能看到通过CSS的作用 My Dashboard section 因为拥有一个 selected class 而高亮显示。因为当前用户已经通过了认证(authentication),所以用户的名字在右上角进行了显示。点击Logout链接。你会看到如下图所示:

在这个页面中,你能看到用户已经登出,然后,你无法看到当前网站的任何菜单。在右上角现在显示的是Log-in链接。

如果在你的登出页面中看到了Django管理站点的登出页面,检查项目settings.py中的INSTALLED_APPS,确保django.contrib.adminaccount应用的后面。每个模板(template)被定位在同样的相对路径时,Django模板(template)读取器将会使用它找到的第一个应用中的模板(templates)。

修改密码视图(views)

我们还需要我们的用户在登录成功后可以进行修改密码。我们要集成Django认证(authentication)视图(views)来修改密码。打开account应用中的urls.py文件,添加如下URL模式:

# change password urls
url(r'^password-change/$',
   'django.contrib.auth.views.password_change',
   name='password_change'),
url(r'^password-change/done/$',
   'django.contrib.auth.views.password_change_done',
   name='password_change_done'),

password_change 视图(view)将会操作表单(form)进行修改密码,当用户成功的修改他的密码后 password_change_done 将会显示一条成功信息。让我们为每个视图(view)创建一个模板(template)。

在你的account应用templates/registration/目录下添加一个新的文件命名为password_change_form,在文件中添加如下代码:

{% extends "base.html" %}
{% block title %}Change you password{% endblock %}
{% block content %}
  <h1>Change you password</h1>
  <p>Use the form below to change your password.</p>
  <form action="." method="post">
    {{ form.as_p }}
    <p><input type="submit" value="Change"></p>
    {% csrf_token %}
  </form>
{% endblock %}

这个模板(template)包含了修改密码的表单(form)。现在,在相同的目录下创建另一个文件,命名为password_change_done.html,为它添加如下代码:

{% extends "base.html" %}
{% block title %}Password changed{% endblock %}
{% block content %}
  <h1>Password changed</h1>
  <p>Your password has been successfully changed.</p>
 {% endblock %}

这个模板(template)只包含显示一条成功的信息 当用户成功的修改他们的密码。

在浏览器中打开 http://127.0.0.1:8000/account/password-change/ 。如果你的用户没有登录,浏览器会重定向你到登录页面。在你成功认证(authentication)登录后,你会看到如下图所示的修改密码页面:

在表单(form)中填写你的旧密码和新密码,然后点击Change按钮。你会看到如下所示的成功页面:

登出再使用新的密码进行登录来验证每件事是否如预期一样工作。

重置密码视图(views)

account应用urls.py文件中为密码重置添加如下URL模式:

# restore password urls
 url(r'^password-reset/$',
    'django.contrib.auth.views.password_reset',
    name='password_reset'),
 url(r'^password-reset/done/$',
     'django.contrib.auth.views.password_reset_done',
     name='password_reset_done'),
  url(r'^password-reset/confirm/(?P<uidb64>[-\w]+)/(?P<token>[-\w]+)/$',
       'django.contrib.auth.views.password_reset_confirm',
     name='password_reset_confirm'),
 url(r'^password-reset/complete/$',
     'django.contrib.auth.views.password_reset_complete',
      name='password_reset_complete'),

在你的account应用templates/registration/目录下添加一个新的文件命名为password_reset_form.html,为它添加如下代码:

{% extends "base.html" %}
{% block title %}Reset your password{% endblock %}
{% block content %}
  <h1>Forgotten your password?</h1>
  <p>Enter your e-mail address to obtain a new password.</p>
  <form action="." method="post">
    {{ form.as_p }}
    <p><input type="submit" value="Send e-mail"></p>
    {% csrf_token %}
  </form>
{% endblock %}

现在,在相同的目录下添加另一个文件命名为password_reset_email.html,为它添加如下代码:

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol }}://{{ domain }}{% url "password_reset_confirm" uidb64=uid token=token %}
Your username, in case you've forgotten: {{ user.get_username }}

这个模板(template)会被用来渲染发送给用户的重置密码邮件。

在相同目录下添加另一个文件命名为password_reset_done.html,为它添加如下代码:

{% extends "base.html" %}
{% block title %}Reset your password{% endblock %}
{% block content %}
  <h1>Reset your password</h1>
  <p>We've emailed you instructions for setting your password.</p>
  <p>If you don't receive an email, please make sure you've entered the address you registered with.</p>
{% endblock %}

再创建另一个模板(template)命名为password_reset_confirm.html,为它添加如下代码:

{% extends "base.html" %}
{% block title %}Reset your password{% endblock %}
{% block content %}
  <h1>Reset your password</h1>
  {% if validlink %}
    <p>Please enter your new password twice:</p>
    <form action="." method="post">
      {{ form.as_p }}
      {% csrf_token %}
      <p><input type="submit" value="Change my password" /></p>
    </form>
  {% else %}
    <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
  {% endif %}
{% endblock %}

在以上模板中,我们将会检查重置链接是否有效。Django重置密码视图(view)会设置这个变量然后将它带入这个模板(template)的上下文环境中。如果重置链接有效,我们展示用户密码重置表单(form)。

创建另一个模板(template)命名为password_reset_complete.html,为它添加如下代码:

{% extends "base.html" %}
{% block title %}Password reset{% endblock %}
{% block content %}
  <h1>Password set</h1>
  <p>Your password has been set. You can <a href="{% url "login" %}">log in now</a></p>
{% endblock %}

最后,编辑account应用中的/registration/login.html模板(template),添加如下代码在<form>元素之后:

<p><a href="{% url "password_reset" %}">Forgotten your password?</a></p>

现在,在浏览器中打开 http://127.0.0.1:8000/account/login/ 然后点击Forgotten your password?链接。你会看到如下图所示页面:

在这部分,你需要在你项目中的settings.py中添加一个SMTP配置,这样Django才能发送e-mails。我们已经在第二章 使用高级特性来优化你的blog学习过如何添加e-mail配置。当然,在开发期间,我们可以配置Django在标准输出中输出e-mail内容来代替通过SMTP服务发送邮件。Django提供一个e-mail后端来输出e-mail内容到控制器中。编辑项目中settings.py文件,添加如下代码:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

EMAIL_BACKEND设置这个类用来发送e-mails。

回到你的浏览器中,填写一个存在用户的e-mail地址,然后点击Send e-mail按钮。你会看到如下图所示页面:

当你运行开发服务的时候看眼控制台输出。你会看到如下所示生成的e-mail:

IME-Version: 1.0
 Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
Subject: Password reset on 127.0.0.1:8000
From: webmaster@localhost
To: user@domain.com
Date: Thu, 24 Sep 2015 14:35:08 -0000
Message-ID: <20150924143508.62996.55653@zenx.local>
Someone asked for password reset for email user@domain.com. Follow the link below:
http://127.0.0.1:8000/account/password-reset/confirm/MQ/45f-9c3f30caafd523055fcc/
Your username, in case you've forgotten: zenx

这封e-mail被我们之前创建的password_reset_email.html模板给渲染。这个给你重置密码的URL带有一个Django动态生成的token。在浏览器中打开这个链接,你会看到如下所示页面:

这个页面对应password_reset_confirm.html模板(template)用来设置新密码。填写新的密码然后点击Change my password button。Django会创建一个新的加密后密码保存进数据库,你会看到如下图所示页面:

现在你可以使用你的新密码登录你的账号。每个用来设置新密码的token只能使用一次。如果你再次打开你之前获取的链接,你会得到一条信息告诉你这个token已经无效了。

你在项目中集成了Django认证(authentication)框架的视图(views)。这些视图(views)对大部分的情况都适合。当然,如果你需要一种不同的行为你能创建你自己的视图。

(译者注:第四章上结束,之后内容将放到第四章下http://www.jianshu.com/p/fd9a6689124d

推荐阅读更多精彩内容