《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.lxweimin.com/p/fd9a6689124d

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容