先決條件: | 完成所有以前的教學(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)限。
當(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. ....
當(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è)新的組。
現(xiàn)在讓我們創(chuàng)建一個(gè)用戶:
而已!。 現(xiàn)在你有一個(gè)"正常的庫(kù)成員"帳戶,你將能夠用于測(cè)試(一旦我們實(shí)現(xiàn)了頁(yè)面,使他們能夠登錄)。
注意:您應(yīng)該嘗試創(chuàng)建其他庫(kù)成員用戶。 另外,為圖書(shū)館員創(chuàng)建一個(gè)組,并向其添加一個(gè)用戶!
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è)谶@里顯示的!
將以下內(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)。
這是用于獲取用戶的電子郵件地址(用于發(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 %}
此表單在您的電子郵件地址收集后顯示。 創(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 %}
此模板提供了我們將發(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 %}
此頁(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 %}
這是最后一個(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 %}
現(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)。
本節(jié)討論我們可以做什么來(lái)根據(jù)用戶是否登錄來(lái)選擇性地控制用戶看到的內(nèi)容。
您可以使用 {{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è)面。
如果使用基于函數(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 。
現(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',) ? ? ? ? }), ? ? )
現(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)目。
現(xiàn)在打開(kāi) /catalog/urls.py ,并添加一個(gè)指向上述視圖的 url()
(您只需將下面的文本復(fù)制到文件末尾即可)。
urlpatterns += [ url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'), ]
現(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è)面)。
最后一步是將這個(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)限與模型相關(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è)教程!
在本文前面,我們向您展示了如何為當(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ù)。
更多建議: