Django Tutorial Part 6: Generic list and detail views

2018-05-15 17:26 更新
先決條件: 完成之前的所有教學(xué)主題,包括 Django教程第5部分:創(chuàng)建我們的主頁(yè)。
目的: 了解在何處以及如何使用基于類的基本視圖,以及如何從URL中提取模式并將信息傳遞給視圖。

概述

在本教程中,我們將完成第一個(gè)版本的 LocalLibrary >通過(guò)添加圖書(shū)和作者的列表和詳細(xì)信息頁(yè)面(或更準(zhǔn)確地說(shuō),我們將向您介紹如何實(shí)現(xiàn)圖書(shū)頁(yè)面,并讓您自己創(chuàng)建作者頁(yè)面)。

該過(guò)程類似于創(chuàng)建索引頁(yè),我們?cè)谏弦粋€(gè)教程中顯示。 我們?nèi)匀恍枰獎(jiǎng)?chuàng)建URL地圖,視圖和模板。 主要的區(qū)別是,對(duì)于詳細(xì)頁(yè)面,我們將有額外的挑戰(zhàn),從URL中的模式提取信息并將其傳遞給視圖。 對(duì)于這些頁(yè)面,我們將演示一個(gè)完全不同類型的視圖:基于類的列表和詳細(xì)視圖。 這些可以顯著減少所需的視圖代碼量,使其更易于編寫(xiě)和維護(hù)。

本教程的最后一部分將演示如何在使用基于類的列表視圖時(shí)對(duì)數(shù)據(jù)進(jìn)行分頁(yè)。

圖書(shū)列表頁(yè)

圖書(shū)列表頁(yè)面將顯示頁(yè)面中所有可用圖書(shū)記錄的列表,使用以下網(wǎng)址訪問(wèn): catalog / books / 該頁(yè)面將顯示每個(gè)記錄的標(biāo)題和作者,標(biāo)題是相關(guān)聯(lián)的圖書(shū)詳細(xì)信息頁(yè)面的超鏈接。 該網(wǎng)頁(yè)與網(wǎng)站中的所有其他網(wǎng)頁(yè)具有相同的結(jié)構(gòu)和導(dǎo)航,因此可以擴(kuò)展我們?cè)谏弦唤坛讨袆?chuàng)建的基本模板( base_generic.html )。

URL映射

打開(kāi) /catalog/urls.py ,然后復(fù)制下面粗體顯示的行。 與我們的索引映射非常類似,這個(gè) url()函數(shù)定義了一個(gè)正則表達(dá)式(RE)來(lái)匹配URL( r\'^ books / $\' 如果URL匹配( views.BookListView.as_view())和此特定映射的名稱,則將調(diào)用此函數(shù)。

urlpatterns = [
    url(r'^$', views.index, name='index'),
?   url(r'^books/$', views.BookListView.as_view(), name='books'),
]

這里的正則表達(dá)式匹配等于 books / ( ^ 是字符串開(kāi)始標(biāo)記, $ 是字符串標(biāo)記的結(jié)尾)的URL。 如前面的教程中所討論的,URL必須已經(jīng)具有匹配的 / catalog ,因此視圖將實(shí)際調(diào)用URL: / catalog / books /

視圖函數(shù)具有與以前不同的格式 - 這是因?yàn)榇艘晥D實(shí)際上將實(shí)現(xiàn)為類。 我們將繼承一個(gè)現(xiàn)有的通用視圖函數(shù),它已經(jīng)完成了我們想要此視圖函數(shù)所做的大部分工作,而不是從頭開(kāi)始編寫(xiě)自己的視圖函數(shù)。

對(duì)于基于Django類的視圖,我們通過(guò)調(diào)用類方法 as_view()來(lái)訪問(wèn)一個(gè)合適的視圖函數(shù)。 這將完成創(chuàng)建類的實(shí)例的所有工作,并確保為傳入的HTTP請(qǐng)求調(diào)用正確的處理程序方法。

視圖(基于類)

我們可以很容易地將書(shū)列表視圖作為常規(guī)函數(shù)(就像我們以前的索引視圖一樣),它將查詢數(shù)據(jù)庫(kù)中的所有書(shū),然后調(diào)用 render()將列表傳遞給 指定模板。 相反,我們將使用基于類的通用列表視圖(ListView) - 從現(xiàn)有視圖繼承的類。 因?yàn)橥ㄓ靡晥D已經(jīng)實(shí)現(xiàn)了我們所需要的大多數(shù)功能,并且遵循Django最佳實(shí)踐,我們將能夠創(chuàng)建一個(gè)更健壯的列表視圖,代碼更少,重復(fù)次數(shù)更少,最終減少維護(hù)。

打開(kāi) catalog / views.py ,然后將以下代碼復(fù)制到文件底部:

from django.views import generic

class BookListView(generic.ListView):
    model = Book

而已! 通用視圖將查詢數(shù)據(jù)庫(kù)以獲取指定模型的所有記錄( Book ),然后渲染位于 /locallibrary/catalog/templates/catalog/book_list.html (我們將在下面創(chuàng)建)。 在模板中,您可以使用名為 object_list book_list 的模板變量訪問(wèn)圖書(shū)列表(即通常為" the_model_name _list 代碼>")。

注意:模板位置的尷尬路徑不是錯(cuò)誤印記 - 通用視圖會(huì)在 / application_name / the_model_name 應(yīng)用程序的 / application_name / templates / 目錄()中的 / list.html ( catalog / book_list.html > / catalog / templates /)。

您可以添加屬性以更改上述默認(rèn)行為。 例如,如果您需要具有使用此相同模型的多個(gè)視圖,則可以指定另一個(gè)模板文件,或者如果 book_list 對(duì)您的特定模板使用不直觀,則可能需要使用不同的模板變量名稱 -案件。 可能最有用的變化是更改/過(guò)濾返回的結(jié)果子集 - 因此,不是列出所有圖書(shū),而是列出其他用戶閱讀的前5個(gè)圖書(shū)。

class BookListView(generic.ListView):
    model = Book
    context_object_name = 'my_book_list'   # your own name for the list as a template variable
    queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
    template_name = 'books/my_arbitrary_template_name_list.html'  # Specify your own template name/location

Overriding methods in class-based views

雖然我們不需要這樣做,你也可以覆蓋一些類方法。

例如,我們可以覆蓋 get_queryset()方法來(lái)更改返回的記錄列表。 這比設(shè)置 queryset 屬性更靈活,就像我們?cè)谇懊娴拇a片段中做的那樣(雖然在這種情況下沒(méi)有真正的好處):

class BookListView(generic.ListView):
    model = Book

? ? def get_queryset(self):
? ? ? ? return Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war

我們還可以覆蓋 get_context_data(),以便將其他上下文變量傳遞給模板(例如,默認(rèn)情況下會(huì)傳遞書(shū)籍列表)。 下面的片段顯示了如何向上下文添加一個(gè)名為"some_data"的變量(它將作為模板變量使用)。

class BookListView(generic.ListView):
    model = Book

? ? def get_context_data(self, **kwargs):
? ? ? ? # Call the base implementation first to get a context
? ? ? ? context = super(BookListView, self).get_context_data(**kwargs)
? ? ? ? # Get the blog from id and add it to the context
? ? ? ? context['some_data'] = 'This is just some data'
? ? ? ? return context

當(dāng)這樣做時(shí),重要的是遵循上面使用的模式:

  • First get the existing context from our superclass.
  • Then add your new context information.
  • Then return the new (updated) context.

注意:查看內(nèi)置 - 在基于類的通用視圖(Django docs)中有更多可以做的事例。

創(chuàng)建列表視圖模板

創(chuàng)建HTML文件 /locallibrary/catalog/templates/catalog/book_list.html ,然后在下面的文本中復(fù)制。 如上所述,這是基于通用類的列表視圖(對(duì)于名為目錄的應(yīng)用程序中名為 Book 的模型)所期望的默認(rèn)模板文件。

通用視圖的模板就像任何其他模板(雖然傳遞給模板的上下文/信息當(dāng)然可能不同)。 與我們的 index 模板一樣,我們?cè)诘谝恍兄袛U(kuò)展我們的基本模板,然后替換名為 content 的塊。

{% extends "base_generic.html" %}

{% block content %}
? ? <h1>Book List</h1>

? ? {% if book_list %}
? ? <ul>

? ? ? {% for book in book_list %}
? ? ? <li>
? ? ? ? <a href="{{ book.get_absolute_url }}">{{ book.title }}</a> ({{book.author}})
? ? ? </li>
? ? ? {% endfor %}

? ? </ul>
? ? {% else %}
? ? ? <p>There are no books in the library.</p>
? ? {% endif %} ? ? ??
{% endblock %}

默認(rèn)情況下,視圖傳遞上下文(書(shū)籍列表)為 object_list book_list (別名;將起作用)。

Conditional execution

我們使用 如果 , else endif 模板標(biāo)簽來(lái)檢查 book_list 是否已定義并且不為空。 它是空的( else 子句),然后我們顯示文本,說(shuō)明沒(méi)有圖書(shū)要顯示。 如果它不為空,那么我們遍歷書(shū)的列表。

{% if book_list %}
  <!-- code here to list the books -->
{% else %}
  <p>There are no books in the library.</p>
{% endif %}

上述條件僅檢查一種情況,但您可以使用 elif 模板標(biāo)記(例如 {%elif var2%} )對(duì)其他條件進(jìn)行測(cè)試。 有關(guān)條件運(yùn)算符的詳情,請(qǐng)參閱:如果 class ="external"> ifequal / ifnotequal https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#ifchanged"class ="external"> ifchanged en / 1.10 / ref / templates / builtins"class ="external">內(nèi)置模板標(biāo)簽和過(guò)濾器(Django Docs)。

For loops

該模板使用 endfor 代碼>模板標(biāo)記循環(huán)訪問(wèn)圖書(shū)列表,如下所示。 每次迭代用當(dāng)前列表項(xiàng)的信息填充 book 模板變量。

{% for book in book_list %}
  <li> <!-- code here get information from each book item --> </li>
{% endfor %}

雖然這里不使用,但在循環(huán)內(nèi)Django還將創(chuàng)建其他變量,您可以使用它來(lái)跟蹤迭代。 例如,您可以測(cè)試 forloop.last 變量,以便在上次運(yùn)行循環(huán)時(shí)執(zhí)行條件處理。

Accessing variables

循環(huán)中的代碼為每本書(shū)創(chuàng)建一個(gè)列表項(xiàng),該列表項(xiàng)顯示標(biāo)題(作為尚未創(chuàng)建的詳細(xì)視圖的鏈接)和作者。

<a href="{{ book.get_absolute_url }}">{{ book.title }}</a> ({{book.author}})

我們使用"點(diǎn)符號(hào)"(例如 book.title book.author )訪問(wèn)關(guān)聯(lián)的圖書(shū)記錄的字段 book 項(xiàng)下是字段名(如模型中定義)。

我們還可以在模板中從模板中調(diào)用函數(shù) - 在這種情況下,我們調(diào)用 Book.get_absolute_url()獲取一個(gè)URL, 。 這個(gè)工作提供的函數(shù)沒(méi)有任何參數(shù)(沒(méi)有辦法傳遞參數(shù)!)

注意:在模板中調(diào)用函數(shù)時(shí),我們必須小心處理"副作用"。 這里我們只是得到一個(gè)顯示的URL,但是一個(gè)函數(shù)可以做任何事情 - 我們不想刪除我們的數(shù)據(jù)庫(kù)(例如)只是通過(guò)渲染我們的模板!

Update the base template

打開(kāi)基本模板( / locallibrary / catalog / templates / base_generic.html ),然后在網(wǎng)址中插入 {%url\'books\'%} 所有圖書(shū)的鏈接,如下所示。 這將啟用所有頁(yè)面的鏈接(我們可以成功地把它放在位,現(xiàn)在我們已經(jīng)創(chuàng)建了"書(shū)籍"URL映射器)。

<li><a href="{% url 'index' %}">Home</a></li>
<li><a href="{% url 'books' %}">All books</a></li>
<li><a href="">All authors</a></li>

它是什么樣子的?

您將無(wú)法構(gòu)建圖書(shū)列表,因?yàn)槲覀內(nèi)匀蝗鄙僖蕾図?xiàng) - 圖書(shū)詳細(xì)信息頁(yè)面的URL地圖,需要?jiǎng)?chuàng)建指向各個(gè)圖書(shū)的超鏈接。 在下一節(jié)后,我們將顯示列表和詳細(xì)信息視圖。

書(shū)詳細(xì)信息頁(yè)

書(shū)詳細(xì)信息頁(yè)將顯示關(guān)于使用網(wǎng)址 catalog / book / < id> (其中 < id> 是圖書(shū)的主鍵)。 除了 Book 模型(作者,摘要,ISBN,語(yǔ)言和流派)中的字段,我們還會(huì)列出可用副本的詳細(xì)信息( BookInstances ), 狀態(tài),預(yù)期的返回日期,印記和ID,這將使我們的讀者不僅了解這本書(shū),而且確認(rèn)是否/何時(shí)可用。

URL映射

打開(kāi) /catalog/urls.py 并添加" book-detail "URL映射器,如下面的粗體所示。 這個(gè) url()函數(shù)定義一個(gè)模式,關(guān)聯(lián)的基于類的詳細(xì)視圖和一個(gè)名稱。

urlpatterns = [
    url(r'^$', views.index, name='index'),
?   url(r'^books/$', views.BookListView.as_view(), name='books'),
?   url(r'^book/(?P<pk>\d+)$', views.BookDetailView.as_view(), name='book-detail'),
]

與我們以前的映射器不同,在這種情況下,我們使用正則表達(dá)式(RE)匹配一個(gè)真正的"模式",而不只是一個(gè)字符串。 這個(gè)特定的RE所做的是與以 book / 開(kāi)頭的任何網(wǎng)址匹配,后跟在行標(biāo)記結(jié)束之前的一個(gè)或多個(gè)數(shù)字(數(shù)字)。 在執(zhí)行匹配時(shí),它"捕獲"數(shù)字,并將它們作為名為 pk 的參數(shù)傳遞給視圖函數(shù)。

注意:如前所述,我們匹配的網(wǎng)址實(shí)際上是 catalog / book /< digits> (因?yàn)槲覀兾挥?strong>目錄應(yīng)用程序 代碼> / catalog / )。

重要:通用基于類的詳細(xì)視圖期望傳遞名為pk的參數(shù)。 如果你正在編寫(xiě)我們自己的函數(shù)視圖,你可以使用任何你喜歡的參數(shù)名,或者確實(shí)在一個(gè)未命名的參數(shù)傳遞信息。

A lightning regular expression primer

正則表達(dá)式是一個(gè)令人難以置信的強(qiáng)大的模式映射工具。 我們之前已經(jīng)避免了很多關(guān)于它們的事情,因?yàn)槲覀冊(cè)赨RL中匹配硬編碼的字符串(而不是模式),并且坦率地說(shuō),它們對(duì)初學(xué)者來(lái)說(shuō)是非常直觀和可怕的。

注意:不要驚慌! 我們將匹配的模式很簡(jiǎn)單,在很多情況下都有很好的文檔!

首先要知道的是,正則表達(dá)式通常應(yīng)該使用原始字符串文字語(yǔ)法來(lái)聲明(即它們?nèi)缦滤? r\')。

聲明模式匹配所需的語(yǔ)法的主要部分是:

符號(hào) 含義
^ 匹配文本的開(kāi)頭
$ 匹配文本的結(jié)尾
\d 匹配數(shù)字(0,1,2,... 9)
\w 匹配字詞字符,例如 字母表中的任何大寫(xiě)或小寫(xiě)字符,數(shù)字或下劃線字符(_)
+ Match one or more of the preceding character. For example, to match one or more digits you would use \d+. To match one or more "a" characters, you could use a+
* Match zero or more of the preceding character. For example, to match nothing or a word you could use \w*
() 捕獲括號(hào)內(nèi)的模式部分。 任何捕獲的值都將作為未命名參數(shù)傳遞到視圖(如果捕獲多個(gè)模式,則相關(guān)參數(shù)將按聲明捕獲的順序提供)。
(?P<name>...) 捕獲模式(由...指示)作為命名變量(在本例中為"名稱")。 捕獲的值將傳遞到具有指定名稱的視圖。 因此,您的視圖必須聲明具有相同名稱的參數(shù)!
[] 與集合中的一個(gè)字符匹配。 例如,[abc]將匹配\'a\'或\'b\'或\'c\'。 [ - \\ w]將匹配" - "字符或任何字字符。

大多數(shù)其他字符可以采取字面意思!

讓我們考慮幾個(gè)真實(shí)的模式例子:

模式 描述
r'^book/(?P<pk>\d+)$'

這是我們的url映射器中使用的RE。 它與在行開(kāi)頭有 book / ( ^ book / )的字符串匹配,然后具有一個(gè)或多個(gè)數(shù)字( \\ d + ),然后結(jié)束(在行標(biāo)記結(jié)束之前沒(méi)有非數(shù)字字符)。

它還捕獲所有數(shù)字(?P \\ d +),并將它們傳遞到名為"pk"的參數(shù)中的視圖。 捕獲的值始終以字符串形式傳遞!

例如,這將匹配 book / 1234 ,并向視圖發(fā)送一個(gè)變量 pk =\'1234\'。

r'^book/(\d+)$' 此匹配與上述情況相同的網(wǎng)址。 捕獲的信息將作為未命名的參數(shù)發(fā)送到視圖。
r'^book/(?P<stub>[-\w]+)$'

此操作與在行開(kāi)頭有 book / ( ^ book / )的字符串匹配,然后有一個(gè)或多個(gè)字符 a" - "或字字符( [ - \\ w] + ),然后結(jié)束。 它還捕獲此組字符,并將它們傳遞到名為"stub"的參數(shù)中的視圖。

這是一個(gè)相當(dāng)?shù)湫偷哪J綖?存根"。 存根是用于數(shù)據(jù)的基于URL的基于字的主鍵。 如果您希望圖書(shū)網(wǎng)址更具信息性,則可以使用存根。 例如 / catalog / book / the-secret-garden 而不是 / catalog / book / 33 。

您可以在一個(gè)匹配中捕獲多個(gè)模式,從而在URL中編碼大量不同的信息。

注意:作為一個(gè)挑戰(zhàn),請(qǐng)考慮如何對(duì)網(wǎng)址進(jìn)行編碼,以列出在特定年份,月份,日期和RE中發(fā)布的所有可用于匹配的圖書(shū)。

Passing additional options in your URL maps

我們?cè)谶@里沒(méi)有使用,但您可能會(huì)發(fā)現(xiàn)有價(jià)值的一個(gè)功能是,您可以聲明并傳遞 views-extra-options"class ="external">其他選項(xiàng)。 選項(xiàng)聲明為一個(gè)字典,作為第三個(gè)未命名的參數(shù)傳遞給 url()函數(shù)。 如果要對(duì)多個(gè)資源使用相同的視圖,并傳遞數(shù)據(jù)以配置其在每種情況下的行為(在每種情況下我們提供不同的模板),此方法可能很有用。

url(r'^/url/$', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
url(r'^/anotherurl/$', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),

注意:將兩個(gè)額外選項(xiàng)和命名捕獲的模式作為命名的參數(shù)傳遞給視圖。 如果對(duì)捕獲的模式和額外選項(xiàng)使用同名,則只會(huì)將捕獲的模式值發(fā)送到視圖(在附加選項(xiàng)中指定的值將被刪除)。

視圖(基于類)

打開(kāi) catalog / views.py ,然后將以下代碼復(fù)制到文件底部:

class BookDetailView(generic.DetailView):
? ? model = Book

而已! 您現(xiàn)在需要做的是創(chuàng)建一個(gè)名為 /locallibrary/catalog/templates/catalog/book_detail.html 的模板,該視圖將為其傳遞特定 Book 的數(shù)據(jù)庫(kù)信息 >記錄由URL映射器提取。 在模板中,您可以使用名為 object book 的模板變量訪問(wèn)書(shū)籍列表(即通常為" the_model_name >")。

如果需要,可以更改模板中使用的模板以及用于引用該書(shū)的上下文對(duì)象的名稱。 您還可以覆蓋方法,例如,向上下文添加附加信息。

What happens if the record doesn't exist?

如果一個(gè)請(qǐng)求的記錄不存在,那么通用的基于類的細(xì)節(jié)視圖將自動(dòng)引發(fā)一個(gè)Http404異常 - 在生產(chǎn)中,這將自動(dòng)顯示一個(gè)適當(dāng)?shù)?找不到資源"頁(yè)面,如果需要可以自定義。

只是為了讓你了解這是如何工作的,下面的代碼片段演示了如何實(shí)現(xiàn)基于類的視圖作為一個(gè)函數(shù),如果你使用通用基于類的細(xì)節(jié)視圖。

def book_detail_view(request,pk):
    try:
        book_id=Book.objects.get(pk=pk)
    except Book.DoesNotExist:
        raise Http404("Book does not exist")

?   #book_id=get_object_or_404(Book, pk=pk)
    
    return render(
        request,
        'catalog/book_detail.html',
        context={'book':book_id,}
    )

視圖首先嘗試從模型中獲取特定的書(shū)記錄。 如果這個(gè)失敗,視圖應(yīng)該引發(fā)一個(gè) Http404 異常,表示該書(shū)"找不到"。 最后一步是像往常一樣,使用模板名稱和 context 參數(shù)(作為字典)調(diào)用 render()。

注意:如果未找到記錄,則 get_object_or_404()(如上所示已注釋掉)是提高 Http404 異常的一個(gè)方便的快捷方式。

創(chuàng)建詳細(xì)視圖模板

創(chuàng)建HTML文件 /locallibrary/catalog/templates/catalog/book_detail.html ,并為其提供以下內(nèi)容。 如上所述,這是基于通用類的 detail 視圖(在名為目錄的應(yīng)用程序中名為 Book 的模型) 代碼>)。

{% extends "base_generic.html" %}

{% block content %}
  <h1>Title: {{ book.title }}</h1>

  <p><strong>Author:</strong> <a href="">{{ book.author }}</a></p> <!-- author detail link not yet defined -->
  <p><strong>Summary:</strong> {{ book.summary }}</p>
  <p><strong>ISBN:</strong> {{ book.isbn }}</p>?
  <p><strong>Language:</strong> {{ book.language }}</p> ?
  <p><strong>Genre:</strong> {% for genre in book.genre.all %} {{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}</p> ?

  <div style="margin-left:20px;margin-top:20px">
    <h4>Copies</h4>

    {% for copy in book.bookinstance_set.all %}
    <hr>
    <p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'd' %}text-danger{% else %}text-warning{% endif %}">{{ copy.get_status_display }}</p>
    {% if copy.status != 'a' %}<p><strong>Due to be returned:</strong> {{copy.due_back}}</p>{% endif %}
    <p><strong>Imprint:</strong> {{copy.imprint}}</p>
    <p class="text-muted"><strong>Id:</strong> {{copy.id}}</p>
    {% endfor %}
  </div>
{% endblock %}

    上面的tempate中的作者鏈接有一個(gè)空的網(wǎng)址,因?yàn)槲覀冞€沒(méi)有創(chuàng)建一個(gè)作者詳細(xì)頁(yè)面。 一旦存在,您應(yīng)該更新URL如下:

    <a href="{% url 'author-detail' book.author.pk %}">{{ book.author }}</a>
    

    雖然有點(diǎn)大,幾乎在這個(gè)模板中的一切都已經(jīng)描述過(guò):

    • We extend our base template and override the "content" block.
    • We use conditional processing to determine whether or not to display specific content.
    • We use for loops to loop through lists of objects.
    • We access the context fields using the dot notation (because we've used the detail generic view, the context is named book; we could also use "object")

    我們之前沒(méi)有看到的一個(gè)有趣的事情是函數(shù) book.bookinstance_set.all()。 這個(gè)方法是由Django"自動(dòng)地"構(gòu)造的,以返回與特定 Book 相關(guān)聯(lián)的 BookInstance 記錄集。

    {% for copy in book.bookinstance_set.all %}
    <!-- code to iterate across each copy/instance of a book -->
    {% endfor %}

    需要此方法,因?yàn)槟辉陉P(guān)系的"一"端聲明了一個(gè) ForeignKey (一對(duì)多)字段。 因?yàn)槟銢](méi)有在其他("many")模型中聲明關(guān)系,所以它沒(méi)有任何字段來(lái)獲取關(guān)聯(lián)記錄的集合。 為了克服這個(gè)問(wèn)題,Django構(gòu)造了一個(gè)可以使用的適當(dāng)命名的"反向查找"函數(shù)。 該函數(shù)的名稱由下殼構(gòu)造,其中 ForeignKey 被聲明的模型名稱后面跟 _set (即在 Book code>是 bookinstance_set())。

    注意:此處我們使用 all()獲取所有記錄(默認(rèn))。 雖然您可以使用 filter()方法獲取代碼中的記錄子集,但是您無(wú)法在模板中直接執(zhí)行此操作,因?yàn)槟荒転楹瘮?shù)指定參數(shù)。

    它是什么樣子的?

    在這一點(diǎn)上,我們應(yīng)該創(chuàng)建顯示圖書(shū)列表和圖書(shū)詳細(xì)頁(yè)面所需的一切。 運(yùn)行服務(wù)器( python3 manage.py runserver ),然后打開(kāi)瀏覽器 http://127.0。 0.1:8000 / 。

    警告:不要點(diǎn)擊任何作者或作者詳細(xì)信息鏈接 - 您將在挑戰(zhàn)中創(chuàng)建這些鏈接!

    點(diǎn)擊所有圖書(shū)鏈接以顯示圖書(shū)列表。

    ; width:823px;">

    然后點(diǎn)擊您的某本書(shū)的鏈接。 如果一切設(shè)置正確,您應(yīng)該看到類似以下屏幕截圖。

    ; width:926px;">

    分頁(yè)

    如果你只有幾個(gè)記錄,我們的書(shū)列表頁(yè)面看起來(lái)很好。 然而,當(dāng)你進(jìn)入幾十或幾百的記錄,頁(yè)面將逐漸加載(和有太多的內(nèi)容,以適當(dāng)瀏覽)。 此問(wèn)題的解決方案是在列表視圖中添加分頁(yè),減少每個(gè)頁(yè)面上顯示的項(xiàng)目數(shù)。

    Django對(duì)分頁(yè)提供了極好的內(nèi)置支持。 更好的是,這是基于類的列表視圖,所以你不必做太多的啟用它!

    視圖

    打開(kāi) catalog / views.py ,然后添加以粗體顯示的 paginate_by 行。

    class BookListView(generic.ListView):
        model = Book
        paginate_by = 10

    使用此添加,只要您有超過(guò)10條記錄,視圖將開(kāi)始分頁(yè)發(fā)送到模板的數(shù)據(jù)。 使用GET參數(shù)訪問(wèn)不同的網(wǎng)頁(yè) - 要訪問(wèn)第2頁(yè),您需要使用網(wǎng)址: / catalog / books / ?page = 2 。

    模板

    現(xiàn)在數(shù)據(jù)已分頁(yè),我們需要添加對(duì)模板的支持以滾動(dòng)瀏覽結(jié)果集。 因?yàn)槲覀兛赡芟朐谒辛斜硪晥D中執(zhí)行此操作,我們將以可添加到基本模板的方式執(zhí)行此操作。

    打開(kāi) / locallibrary / catalog / templates / base_generic.html ,并在我們的內(nèi)容塊下面的下面的分頁(yè)塊中復(fù)制(下面粗體突出顯示)。 代碼首先檢查是否在當(dāng)前頁(yè)面上啟用了分頁(yè)。 如果是,則它適當(dāng)?shù)靥砑酉乱粋€(gè)和上一個(gè)鏈接(以及當(dāng)前頁(yè)碼)。

    {% block content %}{% endblock %}
      
    {% block pagination %}
      {% if is_paginated %}
          <div class="pagination">
              <span class="page-links">
                  {% if page_obj.has_previous %}
                      <a href="{{ request.path }}?page={{ page_obj.previous_page_number }}">previous</a>
                  {% endif %}
                  <span class="page-current">
                      Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
                  </span>
                  {% if page_obj.has_next %}
                      <a href="{{ request.path }}?page={{ page_obj.next_page_number }}">next</a>
                  {% endif %}
              </span>
          </div>
      {% endif %}
    {% endblock %} 

    page_obj Paginator 如果在當(dāng)前頁(yè)面上使用分頁(yè),則將存在的對(duì)象。 它允許您獲取有關(guān)當(dāng)前頁(yè)面,上一頁(yè),有多少頁(yè)面等的所有信息。

    我們使用 {{request.path}} 獲取用于創(chuàng)建分頁(yè)鏈接的當(dāng)前網(wǎng)頁(yè)網(wǎng)址。 這是有用的,因?yàn)樗?dú)立于我們分頁(yè)的對(duì)象。

    而已!

    它是什么樣子的?

    下面的屏幕截圖顯示了分頁(yè)的樣子 - 如果您沒(méi)有在數(shù)據(jù)庫(kù)中輸入超過(guò)10個(gè)標(biāo)題,那么您可以通過(guò)降低 paginate_by 行中指定的數(shù)字更容易地測(cè)試它, strong> catalog / views.py 文件。 為了得到下面的結(jié)果,我們將它改為 paginate_by = 2 。

    分頁(yè)鏈接顯示在底部,根據(jù)您所在的頁(yè)面顯示下一個(gè)/上一個(gè)鏈接。

    ; width:924px;">

    挑戰(zhàn)自己

    本文中的挑戰(zhàn)是創(chuàng)建作者詳細(xì)信息和列出完成項(xiàng)目所需的視圖。 這些應(yīng)在以下網(wǎng)址提供:

    • catalog/authors/?— The list of all authors.
    • catalog/author/<id>?—?The detail view for the specific author?with a primary key field named?<id>

    URL映射器和視圖所需的代碼應(yīng)該與上面創(chuàng)建的 Book 列表和詳細(xì)視圖幾乎相同。 模板將是不同的,但會(huì)共享類似的行為。

    注意:

    • Once you've created the URL mapper for the author list page you will also need to update the All authors link in the base template. Follow the same process as we did when we updated the All books link.
    • Once you've created the URL mapper for the author detail page, you should also?update the book detail view template?(/locallibrary/catalog/templates/catalog/book_detail.html) so that the author link points to your new author detail page (rather than being an empty URL). The line will change to add the template tag shown in bold below.
      <p><strong>Author:</strong> <a href="{% url 'author-detail' book.author.pk %}">{{ book.author }}</a></p> 
      

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

      ; width:825px;">

        概要

        恭喜,我們的基本庫(kù)功能現(xiàn)已完成!

        在本文中,我們學(xué)習(xí)了如何使用基于類的列表和詳細(xì)視圖,并使用它們來(lái)創(chuàng)建頁(yè)面來(lái)查看我們的書(shū)籍和作者。 一路上,我們學(xué)習(xí)了使用正則表達(dá)式的模式匹配,以及如何將數(shù)據(jù)從URL傳遞到視圖。 我們還學(xué)習(xí)了使用模板的更多技巧。 最后,我們展示了如何對(duì)列表視圖進(jìn)行分頁(yè),以便我們的列表是可管理的,即使我們有很多記錄。

        在我們的下一篇文章中,我們將擴(kuò)展此庫(kù)以支持用戶帳戶,從而演示用戶身份驗(yàn)證,權(quán)限,會(huì)話和表單。

        也可以看看

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

        掃描二維碼

        下載編程獅App

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

        編程獅公眾號(hào)