Django Tutorial Part 8: User authentication and permissions

2018-05-15 17:26 更新
先決條件: 完成所有以前的教學(xué)課題,最多包括 Django教程第7部分:會(huì)話框架。
目的: 了解如何設(shè)置和使用用戶身份驗(yàn)證和權(quán)限。

概述

Django提供了一個(gè)認(rèn)證和授權(quán)("權(quán)限")系統(tǒng),該系統(tǒng)構(gòu)建在上一個(gè)教程中討論的會(huì)話框架之上,允許您驗(yàn)證用戶憑據(jù) 并定義每個(gè)用戶允許執(zhí)行的操作。 該框架包括 Users Groups (一次向多個(gè)用戶應(yīng)用權(quán)限的通用方法)的內(nèi)置模型,用于指定用戶 可以執(zhí)行用于登錄用戶的任務(wù),表單和視圖,以及用于限制內(nèi)容的查看工具。

注意:根據(jù)Django,認(rèn)證系統(tǒng)旨在非常通用,因此不提供其他Web認(rèn)證系統(tǒng)中提供的一些功能。 一些常見(jiàn)問(wèn)題的解決方案可用作第三方包。 例如,限制登錄嘗試和針對(duì)第三方的身份驗(yàn)證(例如OAuth)。

在本教程中,我們將向您介紹如何在 LocalLibrary 中啟用用戶身份驗(yàn)證, a>網(wǎng)站,創(chuàng)建您自己的登錄和注銷頁(yè)面,為您的模型添加權(quán)限,以及控制對(duì)頁(yè)面的訪問(wèn)。 我們將使用身份驗(yàn)證/權(quán)限來(lái)顯示為用戶和圖書(shū)館員借用的圖書(shū)列表。

認(rèn)證系統(tǒng)非常靈活,您可以從頭開(kāi)始構(gòu)建您的URL,表單,視圖和模板,只需調(diào)用提供的API登錄用戶即可。 但是,在本文中,我們將使用Django的"stock"認(rèn)證視圖和表單登錄和注銷頁(yè)面。 我們?nèi)匀恍枰獎(jiǎng)?chuàng)建一些模板,但這很容易。

我們還將向您展示如何創(chuàng)建權(quán)限,并檢查視圖和模板中的登錄狀態(tài)和權(quán)限。

啟用身份驗(yàn)證

當(dāng)我們創(chuàng)建骨架網(wǎng)站(在教程2中)時(shí),會(huì)自動(dòng)啟用身份驗(yàn)證,因此您無(wú)需在此時(shí)再執(zhí)行任何操作。

注意:當(dāng)我們使用 django-admin startproject 命令創(chuàng)建應(yīng)用程序時(shí),所有必要的配置都已完成。 當(dāng)我們第一次調(diào)用 python manage.py migrate 時(shí),創(chuàng)建了用戶和模型權(quán)限的數(shù)據(jù)庫(kù)表。

配置在項(xiàng)目文件( locallibrary / locallibrary / settings.py )的 INSTALLED_APPS MIDDLEWARE 部分中設(shè)置,如下所示:

INSTALLED_APPS = [
    ...
? ? 'django.contrib.auth',  #Core authentication framework and its default models.
? ? 'django.contrib.contenttypes',  #Django content type system (allows permissions to be associated with models).
    ....

MIDDLEWARE = [
    ...
? ? 'django.contrib.sessions.middleware.SessionMiddleware',  #Manages sessions across requests
    ...
? ? 'django.contrib.auth.middleware.AuthenticationMiddleware',  #Associates users with requests using sessions.
    ....

創(chuàng)建用戶和組

當(dāng)我們?cè)诮坛?中查看 Django管理網(wǎng)站時(shí),您已經(jīng)創(chuàng)建了第一個(gè)用戶(這是一個(gè)超級(jí)用戶,使用命令 python manage創(chuàng)建)。 py createsuperuser)。 我們的超級(jí)用戶已經(jīng)通過(guò)身份驗(yàn)證并擁有所有權(quán)限,因此我們需要?jiǎng)?chuàng)建一個(gè)測(cè)試用戶來(lái)代表一個(gè)正常的網(wǎng)站用戶。 我們將使用管理網(wǎng)站創(chuàng)建我們的 locallibrary 組和網(wǎng)站登錄,因?yàn)樗亲羁斓姆绞街弧?/span>

注意:您也可以以編程方式創(chuàng)建用戶,如下所示。 你必須這樣做,例如,如果開(kāi)發(fā)一個(gè)接口,允許用戶創(chuàng)建自己的登錄(你不應(yīng)該給用戶訪問(wèn)管理站點(diǎn))。

from django.contrib.auth.models import User

# Create user and save to the database
user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')

# Update fields and then save again
user.first_name = 'John'
user.last_name = 'Citizen'
user.save()

下面我們先創(chuàng)建一個(gè)組,然后創(chuàng)建一個(gè)用戶。 即使我們沒(méi)有任何權(quán)限為我們的庫(kù)成員添加,如果我們需要以后,它會(huì)更容易一次添加到組,而不是單獨(dú)給每個(gè)成員。

啟動(dòng)開(kāi)發(fā)服務(wù)器,并導(dǎo)航到本地網(wǎng)絡(luò)瀏覽器中的管理網(wǎng)站( http://127.0.0.1:8000/ admin / )。 使用您的超級(jí)用戶帳戶的憑據(jù)登錄到網(wǎng)站。 管理網(wǎng)站的頂層顯示所有模型,按"django應(yīng)用程序"排序。 驗(yàn)證和授權(quán)部分中,您可以點(diǎn)擊用戶網(wǎng)上論壇鏈接查看其現(xiàn)有記錄。

; width:661px;">

首先讓我們?yōu)閹?kù)成員創(chuàng)建一個(gè)新的組。

  1. Click the Add button (next to Group) to create a new Group; enter the Name "Library Members" for the group.
  2. We don't need any permissions for the group, so just press SAVE (you will be taken to a list of groups).

現(xiàn)在讓我們創(chuàng)建一個(gè)用戶:

  1. Navigate back to the home page of the admin site
  2. Click the Add button next to Users to open the Add user dialog.
  3. Enter an appropriate Username and Password/Password confirmation for your test user
  4. Press SAVE to create the user.

    The admin site will create the new user and immediately take you to a Change user screen where you can change your username and add information for the User model's optional fields. These fields include the first name, last name, email address, the users status and permissions (only the Active flag should be set). Further down you can specify the user's groups and permissions, and see important dates related to the user (e.g. their join date and last login date).
  5. In the Groups section, select Library Member group from the list of Available groups, and then press the right-arrow between the boxes to move it into the Chosen groups box.
  6. We don't need to do anything else here, so just select SAVE again, to go to the list of users.

而已!。 現(xiàn)在你有一個(gè)"正常的庫(kù)成員"帳戶,你將能夠用于測(cè)試(一旦我們實(shí)現(xiàn)了頁(yè)面,使他們能夠登錄)。

注意:您應(yīng)該嘗試創(chuàng)建其他庫(kù)成員用戶。 另外,為圖書(shū)館員創(chuàng)建一個(gè)組,并向其添加一個(gè)用戶!

設(shè)置您的身份驗(yàn)證視圖

Django提供了幾乎所有你需要?jiǎng)?chuàng)建的認(rèn)證頁(yè)面來(lái)處理登錄,注銷和密碼管理"開(kāi)箱即用"。 這包括一個(gè)url映射器,視圖和表單,但它不包括模板 - 我們必須創(chuàng)建自己的!

在本節(jié)中,我們將介紹如何將默認(rèn)系統(tǒng)集成到LocalLibrary網(wǎng)站并創(chuàng)建模板。 我們將它們放在主項(xiàng)目URL中。

注意:您不必使用任何此類代碼,但很可能是您想要的,因?yàn)樗故虑樽兊酶菀住?/span> 如果您更改用戶模型(高級(jí)主題!),您幾乎肯定需要更改表單處理代碼,但即使如此,您仍然可以使用股票視圖功能。

請(qǐng)注意:在這種情況下,我們可以合理地將驗(yàn)證頁(yè)(包括網(wǎng)址和模板)放入我們的目錄應(yīng)用程序中。 但是,如果我們有多個(gè)應(yīng)用程序,最好分離出這種共享登錄行為,并使其可用于整個(gè)網(wǎng)站,這是我們?cè)谶@里顯示的!

項(xiàng)目網(wǎng)址

將以下內(nèi)容添加到項(xiàng)目urls.py文件( locallibrary / locallibrary / urls.py )文件的底部:

#Add Django site authentication urls (for login, logout, password management)
urlpatterns += [
    url('^accounts/', include('django.contrib.auth.urls')),
]

導(dǎo)航到 http://127.0.0.1:8000/accounts/ 網(wǎng)址(請(qǐng)注意尾部正斜杠 !)和Django將顯示一個(gè)錯(cuò)誤,它找不到這個(gè)URL,并列出了所有的URL。 從中可以看到可以工作的網(wǎng)址,例如:

^accounts/ ^login/$ [name='login']
^accounts/ ^logout/$ [name='logout']
^accounts/ ^password_change/$ [name='password_change']
^accounts/ ^password_change/done/$ [name='password_change_done']
^accounts/ ^password_reset/$ [name='password_reset']
^accounts/ ^password_reset/done/$ [name='password_reset_done']
^accounts/ ^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']
^accounts/ ^reset/done/$ [name='password_reset_complete']

現(xiàn)在,嘗試導(dǎo)航到登錄網(wǎng)址( http://127.0.0.1:8000/accounts/login/ / a>)。 此操作會(huì)再次失敗,但出現(xiàn)錯(cuò)誤,告知您我們?cè)谀0逅阉髀窂街腥鄙俦匦璧哪0? registration / login.html )。 您會(huì)在頂部的黃色部分看到以下行:

Exception Type:    TemplateDoesNotExist
Exception Value:    registration/login.html

下一步是在搜索路徑上創(chuàng)建注冊(cè)目錄,然后添加 login.html 文件。

模板目錄

我們剛才添加的url(和隱式視圖)希望在模板搜索路徑中的某個(gè)位置的 / registration / 目錄中找到它們相關(guān)的模板。 對(duì)于此網(wǎng)站,我們會(huì)將模板放置在 templates / registration / 目錄中(即將模板放置在最上層目錄 locallibrary 文件夾) - 請(qǐng)立即創(chuàng)建模板和注冊(cè)目錄。

要使這些目錄對(duì)模板加載器可見(jiàn)(即將此目錄放在模板搜索路徑中),請(qǐng)打開(kāi)項(xiàng)目設(shè)置( /locallibrary/locallibrary/settings.py ),然后更新 TEMPLATES 部分的\'DIRS\'行,如圖所示。

TEMPLATES = [
    {
        ...
        'DIRS': ['./templates',],
        'APP_DIRS': True,
        ...

登錄模板

重要:本文中提供的身份驗(yàn)證模板是Django演示登錄模板的非?;?稍微修改的版本。 您可能需要自定義它們?yōu)槟约菏褂茫?/span>

創(chuàng)建一個(gè)名為/ locallibrary / templates / registration / login.html 的新HTML文件。 給它以下內(nèi)容:

{% extends "base_generic.html" %}

{% block content %}

{% if form.errors %}
<p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
? ? {% if user.is_authenticated %}
? ? <p>Your account doesn't have access to this page. To proceed,
? ? please login with an account that has access.</p>
? ? {% else %}
? ? <p>Please login to see this page.</p>
? ? {% endif %}
{% endif %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}

<div>
? <td>{{ form.username.label_tag }}</td>
? <td>{{ form.username }}</td>
</div>
<div>
? <td>{{ form.password.label_tag }}</td>
? <td>{{ form.password }}</td>
</div>

<div>
? <input type="submit" value="login" />
? <input type="hidden" name="next" value="" />
</div>
</form>

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}

此模板與我們之前看到的模板有一些相似之處 - 它擴(kuò)展了我們的基本模板,并覆蓋了 content 塊。 其余的代碼是相當(dāng)標(biāo)準(zhǔn)的表單處理代碼,我們將在后面的教程中討論。 您現(xiàn)在需要知道的是,這將顯示一個(gè)窗體,您可以在其中輸入您的用戶名和密碼,如果您輸入無(wú)效的值,將提示您在頁(yè)面刷新時(shí)輸入正確的值。

返回登錄頁(yè)面( http://127.0.0.1:8000/accounts/login/ >),一旦您保存了模板,您應(yīng)該看到這樣:

; width:441px;">

如果您嘗試登錄將成功,您將被重定向到另一個(gè)頁(yè)面(默認(rèn)情況下,這將是 http ://127.0.0.1:8000 / accounts / profile / )。 這里的問(wèn)題是,默認(rèn)情況下Django期望在登錄后,你會(huì)想要到一個(gè)配置文件頁(yè)面,這可能是或不是這種情況。 由于您尚未定義此網(wǎng)頁(yè),因此您會(huì)收到另一個(gè)錯(cuò)誤訊息!

打開(kāi)項(xiàng)目設(shè)置( /locallibrary/locallibrary/settings.py ),然后將下面的文字添加到底部。 現(xiàn)在,當(dāng)您登錄時(shí),您應(yīng)該被重定向到網(wǎng)站主頁(yè)默認(rèn)情況下。

# Redirect to home URL after login (Default redirects to /accounts/profile/)
LOGIN_REDIRECT_URL = '/'

注銷模板

如果您導(dǎo)航到注銷網(wǎng)址( http://127.0.0.1:8000/accounts/logout/ a>),那么您會(huì)看到一些奇怪的行為 - 您的用戶將被確實(shí)注銷,但您會(huì)轉(zhuǎn)到管理注銷頁(yè)面。 這不是你想要的,如果只是因?yàn)樵擁?yè)面上的登錄鏈接會(huì)轉(zhuǎn)到管理員登錄屏幕(只有擁有 is_staff 權(quán)限的用戶才能使用)。

創(chuàng)建并打開(kāi)/ locallibrary / templates / registration / logged_out.html 。 在下面的文本中復(fù)制:

{% extends "base_generic.html" %}

{% block content %}
<p>Logged out!</p>  

<a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}

這個(gè)模板很簡(jiǎn)單。 它只是顯示一條消息,通知您已注銷,并提供一個(gè)鏈接,您可以按返回登錄屏幕。 如果再次訪問(wèn)注銷URL,您應(yīng)該會(huì)看到此頁(yè)面:

; width:385px;">

密碼重置模板

默認(rèn)密碼重置系統(tǒng)使用電子郵件向用戶發(fā)送重置鏈接。 您需要?jiǎng)?chuàng)建表單以獲取用戶的電子郵件地址,發(fā)送電子郵件,允許他們輸入新密碼,并在整個(gè)過(guò)程完成時(shí)注意。

以下模板可用作起點(diǎn)。

Password reset form

這是用于獲取用戶的電子郵件地址(用于發(fā)送密碼重置電子郵件)的表單。 創(chuàng)建 /locallibrary/templates/registration/password_reset_form.html ,并提供以下內(nèi)容:

{% extends "base_generic.html" %}
{% block content %}

<form action="" method="post">{% csrf_token %}
    {% if form.email.errors %} {{ form.email.errors }} {% endif %}
        <p>{{ form.email }}</p> 
    <input type="submit" class="btn btn-default btn-lg" value="Reset password" />
</form>

{% endblock %}

Password reset done

此表單在您的電子郵件地址收集后顯示。 創(chuàng)建 /locallibrary/templates/registration/password_reset_done.html ,并提供以下內(nèi)容:

{% extends "base_generic.html" %}
{% block content %}
<p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}

Password reset email

此模板提供了我們將發(fā)送給用戶的HTML電子郵件的文本,其中包含重置鏈接。 創(chuàng)建 /locallibrary/templates/registration/password_reset_email.html ,并提供以下內(nèi)容:

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

Password reset confirm

此頁(yè)面是您在單擊密碼重置電子郵件中的鏈接后輸入新密碼的位置。 創(chuàng)建 /locallibrary/templates/registration/password_reset_confirm.html ,并提供以下內(nèi)容:

{% extends "base_generic.html" %}

{% block content %}

    {% if validlink %}
        <p>Please enter (and confirm) your new password.</p>
        <form action="" method="post">
            <div style="display:none">
                <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken">
            </div>
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset failed</h1>
        <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
    {% endif %}

{% endblock %}

Password reset complete

這是最后一個(gè)密碼重置模板,顯示該模板以在密碼重置成功時(shí)通知您。 創(chuàng)建 /locallibrary/templates/registration/password_reset_complete.html ,并提供以下內(nèi)容:

{% extends "base_generic.html" %}
{% block content %}

<h1>The password has been changed!</h1>
<p><a href="{% url 'login' %}">log in again?</a></p>

{% endblock %}

測(cè)試新的身份驗(yàn)證頁(yè)面

現(xiàn)在您已添加了網(wǎng)址配置并創(chuàng)建了所有這些模板,驗(yàn)證頁(yè)面現(xiàn)在應(yīng)該可以正常工作了!

您可以嘗試使用以下網(wǎng)址登錄并注銷超級(jí)用戶帳戶,以測(cè)試新的身份驗(yàn)證頁(yè):

您可以從登錄頁(yè)面的鏈接中測(cè)試密碼重置功能。 請(qǐng)注意,Django只會(huì)向已存儲(chǔ)在其數(shù)據(jù)庫(kù)中的地址(用戶)發(fā)送重置電子郵件!

注意:密碼重置系統(tǒng)要求您的網(wǎng)站支持電子郵件,這超出了本文的范圍,因此此部分無(wú)法使用 要允許測(cè)試,請(qǐng)將以下行放在settings.py文件的末尾。 這會(huì)記錄發(fā)送到控制臺(tái)的任何電子郵件(因此您可以從控制臺(tái)復(fù)制密碼重置鏈接)。

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

有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn)發(fā)送電子郵件(Django docs)。

針對(duì)經(jīng)過(guò)身份驗(yàn)證的用戶進(jìn)行測(cè)試

本節(jié)討論我們可以做什么來(lái)根據(jù)用戶是否登錄來(lái)選擇性地控制用戶看到的內(nèi)容。

在模板中測(cè)試

您可以使用 {{user}} 模板變量獲取模板中當(dāng)前登錄用戶的信息(當(dāng)我們?cè)谖覀兊墓羌苤性O(shè)置項(xiàng)目時(shí),默認(rèn)將其添加到模板上下文中 )。

通常,您將首先針對(duì) {{user.is_authenticated}} 模板變量進(jìn)行測(cè)試,以確定用戶是否有資格查看特定內(nèi)容。 為了演示這一點(diǎn),接下來(lái)我們將更新側(cè)邊欄,如果用戶已注銷則顯示"登錄"鏈接,如果他們已登錄,則顯示"注銷"鏈接。

打開(kāi)基本模板( /locallibrary/catalog/templates/base_generic.html ),然后將以下文本復(fù)制到 sidebar 塊中緊挨 endblock >模板標(biāo)簽。

  <ul class="sidebar-nav">

    ...

   {% if user.is_authenticated %}
     <li>User: {{ user.get_username }}</li>
     <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>   
   {% else %}
     <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>   
   {% endif %} 
  </ul>

如您所見(jiàn),我們使用 if - else - endif 模板標(biāo)簽有條件地顯示文本,基于 {{user.is_authenticated }} 為true。 如果用戶通過(guò)身份驗(yàn)證,我們知道我們有一個(gè)有效的用戶,因此我們調(diào)用 {{user.get_username}} 來(lái)顯示他們的名字。

我們使用 url 模板標(biāo)記和相應(yīng)網(wǎng)址配置的名稱創(chuàng)建登錄和注銷鏈接網(wǎng)址。 還要注意我們?nèi)绾卧赨RL的末尾附加?next = {{request.path}} 此操作是添加包含當(dāng)前頁(yè)面的地址(URL)的下一步 ,到鏈接的URL的結(jié)尾。 在用戶成功登錄/注銷后,視圖將使用此" next "值將用戶重定向到他們首次單擊登錄/注銷鏈接的頁(yè)面。

注意:嘗試一下! 如果您在主頁(yè)上,并單擊側(cè)邊欄中的登錄/注銷,則操作完成后,您應(yīng)該返回到同一頁(yè)面。

視圖中的測(cè)試

如果使用基于函數(shù)的視圖,限制對(duì)函數(shù)的訪問(wèn)的最簡(jiǎn)單的方法是將 login_required 裝飾器應(yīng)用于視圖函數(shù),如下所示。 如果用戶登錄,那么您的視圖代碼將正常執(zhí)行。 如果用戶未登錄,則將重定向到項(xiàng)目設(shè)置( settings.LOGIN_URL )中定義的登錄URL,將當(dāng)前絕對(duì)路徑作為 next URL參數(shù)傳遞 。 如果用戶成功登錄,則他們將返回此頁(yè)面,但這次被驗(yàn)證。

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

注意:您可以通過(guò)在 request.user.is_authenticated 上測(cè)試手動(dòng)完成相同類型的事情,但裝飾器更方便!

同樣,在基于類的視圖中限制對(duì)已登錄用戶的訪問(wèn)的最簡(jiǎn)單方法是從 LoginRequiredMixin 派生。 您需要在超類列表中,在主視圖類之前首先聲明此混合。

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, View):
    ...

這具有與 login_required 裝飾器完全相同的重定向行為。 您還可以指定用戶重定向的替代位置(如果未通過(guò)身份驗(yàn)證( login_url )和URL參數(shù)名稱而不是" next "以插入當(dāng)前絕對(duì) 路徑( redirect_field_name )。

class MyView(LoginRequiredMixin, View):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'

有關(guān)其他詳細(xì)信息,請(qǐng)查看 > Django docs here 。

示例 - 列出當(dāng)前用戶的圖書(shū)

現(xiàn)在我們知道如何將頁(yè)面限制為特定用戶,讓我們創(chuàng)建當(dāng)前用戶借用的書(shū)籍的視圖。

不幸的是,我們還沒(méi)有辦法讓用戶借書(shū)! 因此,在我們創(chuàng)建書(shū)籍列表之前,我們首先擴(kuò)展 BookInstance 模型,以支持借用的概念,并使用Django Admin應(yīng)用程序?qū)⒁恍?shū)籍借給我們的測(cè)試用戶。

楷模

首先,我們必須讓用戶有一個(gè) BookInstance (我們已經(jīng)有一個(gè)狀態(tài) due_back 日期 ,但是我們還沒(méi)有在這個(gè)模型和用戶之間有任何關(guān)聯(lián),我們將使用 ForeignKey (一對(duì)多)字段創(chuàng)建一個(gè),我們還需要一個(gè)簡(jiǎn)單的機(jī)制來(lái)測(cè)試 一本借來(lái)的書(shū)已經(jīng)逾期了。

打開(kāi) catalog / models.py ,然后從 django.contrib.auth.models 中導(dǎo)入 User 在文件的頂部,因此 User 可用于使用它的后續(xù)代碼):

from django.contrib.auth.models import User

接下來(lái)將 borrower 字段添加到 BookInstance 模型中:

borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

當(dāng)我們?cè)谶@里時(shí),我們添加一個(gè)屬性,我們可以從我們的模板調(diào)用,以告訴特定的書(shū)實(shí)例是否過(guò)期。 雖然我們可以在模板本身計(jì)算,使用如下所示的屬性將更加高效。

from datetime import date

@property
def is_overdue(self):
    if date.today() > self.due_back:
        return True
    return False

現(xiàn)在我們更新了模型,我們需要對(duì)項(xiàng)目進(jìn)行新的遷移,然后應(yīng)用這些遷移:

python3 manage.py makemigrations
python3 manage.py migrate

管理員

現(xiàn)在打開(kāi) catalog / admin.py ,并將 borrower 字段添加到 list_display 中的 BookInstanceAdmin fieldsets ,如下所示。 這將使該字段在管理員部分中可見(jiàn),以便我們可以在需要時(shí)將 User 分配給 BookInstance 。

@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
? ? list_display = ('book', 'status', 'borrower', 'due_back', 'id')
? ? list_filter = ('status', 'due_back')
? ??
? ? fieldsets = (
? ? ? ? (None, {
? ? ? ? ? ? 'fields': ('book','imprint', 'id')
? ? ? ? }),
? ? ? ? ('Availability', {
? ? ? ? ? ? 'fields': ('status', 'due_back','borrower',)
? ? ? ? }),
? ? )

貸了幾本書(shū)

現(xiàn)在,它可以借書(shū)給特定的用戶,去借出一些 BookInstance 記錄(設(shè)置他們的借用字段給你的測(cè)試用戶,使 狀態(tài)"貸款"并在未來(lái)和過(guò)去設(shè)置到期日。

注意:我們不會(huì)拼寫此過(guò)程,因?yàn)槟呀?jīng)知道如何使用管理網(wǎng)站!

在貸款視圖

現(xiàn)在,我們將添加一個(gè)視圖,以獲取已借給當(dāng)前用戶的所有圖書(shū)的列表。 我們將使用我們熟悉的相同的通用基于類的列表視圖,但是這次我們還將從 LoginRequiredMixin 導(dǎo)入和派生,以便只有已登錄的用戶可以調(diào)用此視圖。 我們還將選擇聲明一個(gè) template_name ,而不是使用默認(rèn)值,因?yàn)槲覀冏罱K可能會(huì)有一些不同的BookInstance記錄列表,以及不同的視圖和模板。

將以下內(nèi)容添加到catalog / views.py中:

from django.contrib.auth.mixins import LoginRequiredMixin

class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
    """
    Generic class-based view listing books on loan to current user. 
    """
    model = BookInstance
    template_name ='catalog/bookinstance_list_borrowed_user.html'
    paginate_by = 10
    
    def get_queryset(self):
        return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')

為了將我們的查詢限制為當(dāng)前用戶的BookInstance對(duì)象,我們?nèi)缟纤局匦聦?shí)現(xiàn) get_queryset() 請(qǐng)注意,"o"是"借出"的存儲(chǔ)代碼,我們通過(guò) due_back 日期排序,以便先顯示最早的項(xiàng)目。

貸款簿的URL conf

現(xiàn)在打開(kāi) /catalog/urls.py ,并添加一個(gè)指向上述視圖的 url()(您只需將下面的文本復(fù)制到文件末尾即可)。

urlpatterns += [   
    url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
]

貸款書(shū)的模板

現(xiàn)在我們需要為這個(gè)頁(yè)面做的是添加一個(gè)模板。 首先,創(chuàng)建模板文件 /catalog/templates/catalog/bookinstance_list_borrowed_user.html ,并提供以下內(nèi)容:

{% extends "base_generic.html" %}

{% block content %}
    <h1>Borrowed books</h1>

    {% if bookinstance_list %}
    <ul>

      {% for bookinst in bookinstance_list %} 
      <li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
        <a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }})        
      </li>
      {% endfor %}
    </ul>

    {% else %}
      <p>There are no books borrowed.</p>
    {% endif %}       
{% endblock %}

此模板非常類似于之前為 Book Author 對(duì)象創(chuàng)建的模板。 這里唯一的"新"是我們檢查在模型(bookinst.is_overdue )中添加的方法,并使用它來(lái)改變過(guò)期項(xiàng)目的顏色。

當(dāng)開(kāi)發(fā)服務(wù)器運(yùn)行時(shí),您現(xiàn)在應(yīng)該可以在瀏覽器中查看登錄用戶的列表, > http://127.0.0.1:8000/catalog/mybooks/ 。 嘗試這與您的用戶登錄并注銷(在第二種情況下,您應(yīng)該被重定向到登錄頁(yè)面)。

將列表添加到側(cè)欄

最后一步是將這個(gè)新頁(yè)面的鏈接添加到側(cè)邊欄。 我們將把它放在我們顯示已登錄用戶的其他信息的同一部分。

打開(kāi)基本模板( /locallibrary/catalog/templates/base_generic.html ),然后將粗線添加到側(cè)邊欄,如圖所示。

 <ul class="sidebar-nav">
   {% if user.is_authenticated %}
   <li>User: {{ user.get_username }}</li>
   <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li>
   <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>   
   {% else %}
   <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>   
   {% endif %} 
 </ul>

它是什么樣子的?

當(dāng)任何用戶登錄時(shí),他們會(huì)在側(cè)邊欄中看到我的借閱鏈接,并且顯示的書(shū)籍列表如下(第一本書(shū)沒(méi)有到期日,這是我們希望的錯(cuò)誤 在后面的教程中修復(fù)!)。

; width:530px;">

權(quán)限

權(quán)限與模型相關(guān)聯(lián),并定義具有權(quán)限的用戶可以在模型實(shí)例上執(zhí)行的操作。 默認(rèn)情況下,Django會(huì)自動(dòng)為所有模型提供添加,更改權(quán)限,允許具有相關(guān)權(quán)限的用戶通過(guò) 管理網(wǎng)站。 您可以為模型定義自己的權(quán)限,并將其授予特定用戶。 您還可以更改與同一模型的不同實(shí)例關(guān)聯(lián)的權(quán)限。

測(cè)試視圖和模板中的權(quán)限非常類似,用于測(cè)試身份驗(yàn)證狀態(tài)(實(shí)際上,測(cè)試權(quán)限也會(huì)測(cè)試身份驗(yàn)證)。

楷模

使用 permissions 字段在模型" class Meta "中定義權(quán)限。 您可以在元組中指定任意數(shù)量的權(quán)限,每個(gè)權(quán)限本身在包含權(quán)限名稱和權(quán)限顯示值的嵌套元組中定義。 例如,我們可以定義一個(gè)權(quán)限,以允許用戶標(biāo)記圖書(shū)已返回,如圖所示:

class BookInstance(models.Model):
    ...
?   class Meta:
?       ...
        permissions = (("can_mark_returned", "Set book as returned"),)   

然后,我們可以將權(quán)限分配給管理網(wǎng)站中的"圖書(shū)管理員"組。

打開(kāi) catalog / models.py ,然后添加如上所示的權(quán)限。 您需要重新運(yùn)行遷移(調(diào)用 python3 manage.py makemigrations python3 manage.py migrate )以適當(dāng)?shù)馗聰?shù)據(jù)庫(kù)。

模板

當(dāng)前用戶的權(quán)限存儲(chǔ)在名為 {{perms}} 的模板變量中。 您可以使用相關(guān)聯(lián)的Django"應(yīng)用"中的特定變量名稱來(lái)檢查當(dāng)前用戶是否具有特定權(quán)限。 如果用戶具有此權(quán)限,則 {{perms.catalog.can_mark_returned}} 將為 True ,否則為 False 。 我們通常使用模板 {%if%} 來(lái)測(cè)試權(quán)限,如下所示:

{% if perms.catalog.can_mark_returned %}
    <!-- We can mark a BookInstance as returned. -->
?   <!-- Perhaps add code to link to a "book return" view here. -->
{% endif %}

視圖

權(quán)限可以在函數(shù)視圖中使用 permission_required 裝飾器或在基于類的視圖中使用 PermissionRequiredMixin 來(lái)測(cè)試。 模式和行為與登錄身份驗(yàn)證相同,但當(dāng)然您可能需要添加多個(gè)權(quán)限。

函數(shù)視圖裝飾器:

from django.contrib.auth.decorators import permission_required

@permission_required('catalog.can_mark_returned')
@permission_required('catalog.can_edit')
def my_view(request):
    ...

基于類的視圖的權(quán)限所需的混合。

from django.contrib.auth.mixins import PermissionRequiredMixin

class MyView(PermissionRequiredMixin, View):
    permission_required = 'catalog.can_mark_returned'
    # Or multiple of permissions:
    permission_required = ('catalog.can_mark_returned', 'catalog.can_edit')

例子

我們不會(huì)在此更新 LocalLibrary ; 也許在下一個(gè)教程!

挑戰(zhàn)自己

在本文前面,我們向您展示了如何為當(dāng)前用戶創(chuàng)建一個(gè)頁(yè)面,列出他們借用的書(shū)籍。 現(xiàn)在的挑戰(zhàn)是創(chuàng)建一個(gè)類似的頁(yè)面,該頁(yè)面僅對(duì)圖書(shū)館員可見(jiàn),顯示已借用的所有圖書(shū),其中包括每位借款人的姓名。

您應(yīng)該能夠遵循與其他視圖相同的模式。 主要區(qū)別是,您需要將視圖限制為只有圖書(shū)館員。 您可以根據(jù)用戶是否是工作人員(函數(shù)裝飾: staff_member_required ,模板變量: user.is_staff )來(lái)執(zhí)行此操作,但建議您改用 > can_mark_returned 權(quán)限和 PermissionRequiredMixin ,如上一節(jié)所述。

重要:記住不要使用超級(jí)用戶進(jìn)行基于權(quán)限的測(cè)試(即使尚未定義權(quán)限,權(quán)限檢查也始終對(duì)超級(jí)用戶返回true)。 而是創(chuàng)建庫(kù)管理器用戶,并添加所需的功能。

完成后,您的網(wǎng)頁(yè)應(yīng)該如下面的屏幕截圖所示。

; width:500px;">

    概要

    優(yōu)秀的工作 - 你現(xiàn)在創(chuàng)建了一個(gè)網(wǎng)站,圖書(shū)館成員可以登錄并查看自己的內(nèi)容,圖書(shū)館員(具有正確的權(quán)限)可以使用查看所有借出的圖書(shū)和他們的借款人。 目前我們?nèi)匀恢皇遣榭磧?nèi)容,但是當(dāng)您想要開(kāi)始修改和添加數(shù)據(jù)時(shí),使用相同的原理和技術(shù)。

    在下一篇文章中,我們將討論如何使用Django表單收集用戶輸入,然后開(kāi)始修改一些存儲(chǔ)的數(shù)據(jù)。

    也可以看看

    以上內(nèi)容是否對(duì)您有幫助:
    在線筆記
    App下載
    App下載

    掃描二維碼

    下載編程獅App

    公眾號(hào)
    微信公眾號(hào)

    編程獅公眾號(hào)