本教程擴充了 LocalLibrary 網(wǎng)站,為書本與作者增加列表與細節(jié)頁面。此處我們將學到通用類別視圖,并演示如何降低你必須為一般使用案例撰寫的程式碼數(shù)量。我們也會更加深入URL處理細節(jié),演示如何實施基本模式匹配。 本教程中,通過為書本和作者添加列表和詳細信息頁面,我們將完成第一個版本的LocalLibrary 網(wǎng)站(或者更準確地說,我們將向您展示如何實現(xiàn)書頁,并讓您自己創(chuàng)建作者頁面!) 該過程類似于創(chuàng)建索引頁面,我們在上一個教程中展示了該頁面。我們?nèi)匀恍枰獎?chuàng)建URL地圖,視圖和模板。主要區(qū)別在于,對于詳細信息頁面,我們還有一個額外的挑戰(zhàn),即從URL中的模式中提取信息,并將其傳遞給視圖。對于這些頁面,我們將演示一種完全不同的視圖類型:基于類別的通用列表和詳細視圖。這些可以顯著減少所需的視圖代碼量,使其更易于編寫和維護。 本教程的最后一部分,將演示在使用基于類別的通用列表視圖時,如何對數(shù)據(jù)進行分頁。 書本清單頁面,將顯示頁面中所有可用圖書記錄的列表,使用url: catalog/books/ 進行訪問。該頁面將顯示每條記錄的標題和作者,標題是指向相關(guān)圖書詳細信息頁面的超鏈接。該頁面將具有與站點中,所有其他頁面相同的結(jié)構(gòu)和導航,因此,我們可以擴展在上一個教程中創(chuàng)建的基本模板(base_generic.html)。 打開/catalog/urls.py ,并復制到下面粗體顯示的行中。就像索引頁面的方式,這個path() 函數(shù),定義了一個與 URL 匹配的模式('books/'),如果URL匹配,將調(diào)用視圖函數(shù)(views.BookListView.as_view() )和一個對應這個特定映射的名稱。 urlpatterns = [
path('', views.index, name='index'), path('books/', views.BookListView.as_view(), name='books'),]
正如前一個教程中所討論的,URL 必須已經(jīng)先匹配了/catalog ,因此實際上將為 URL 調(diào)用的視圖是:/catalog/books/ 。 視圖函數(shù)具有與以前不同的格式 - 這是因為該視圖,實際上將以類別來實現(xiàn)。我們將繼承現(xiàn)有的泛型視圖函數(shù),該函數(shù)已經(jīng)完成了我們希望此視圖函數(shù)執(zhí)行的大部分工作,而不是從頭開始編寫自己的函數(shù)。對于基于Django類的視圖,我們通過調(diào)用類方法as_view() ,來訪問適當?shù)囊晥D函數(shù)。這樣做可以創(chuàng)建類的實例,并確保為傳入的 HTTP 請求調(diào)用正確的處理程序方法。 我們可以很容易地,將書本列表視圖編寫為常規(guī)函數(shù)(就像我們之前的索引視圖一樣),它將查詢數(shù)據(jù)庫中的所有書本,然后調(diào)用render() ,將列表傳遞給指定的模板。然而,我們用另一種方法取代,我們將使用基于類的通用列表視圖(ListView ) - 一個繼承自現(xiàn)有視圖的類。因為通用視圖,已經(jīng)實現(xiàn)了我們需要的大部分功能,并且遵循 Django 最佳實踐,我們將能夠創(chuàng)建更強大的列表視圖,代碼更少,重復次數(shù)更少,最終維護更少。 打開 catalog/views.py,并將以下代碼復制到文件的底部: from django.views import generic
class BookListView(generic.ListView):
model = Book
就是這樣!通用視圖將查詢數(shù)據(jù)庫,以獲取指定模型(Book )的所有記錄,然后呈現(xiàn)位于/locallibrary/catalog/templates/catalog/book_list.html 的模板(我們將在下面創(chuàng)建)。在模板中,您可以使用名為object_list 或 book_list 的模板變量(即通常為“the_model_name_list ”),以訪問書本列表。 注意: 模板位置的這個尷尬路徑不是印刷錯誤 - 通用視圖在應用程序的/application_name/templates/ 目錄中(/catalog/templates/ ),查找模板/application_name/the_model_name_list.html (在本例中為catalog/book_list.html )。 您可以添加屬性,以更改上面的默認行為。例如,如果需要使用同一模型的多個視圖,則可以指定另一個模板文件,或者如果book_list 對于特定模板用例不直觀,則可能需要使用不同的模板變量名稱??赡茏钣杏玫淖兏?,是更改/過濾返回的結(jié)果子集 - 因此,您可能會列出其他用戶閱讀的前5本書,而不是列出所有書本。 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
覆蓋基于類別的視圖中的方法雖然我們不需要在這里執(zhí)行此操作,但您也可以覆蓋某些類別方法。 例如,我們可以覆蓋get_queryset() 方法,來更改返回的記錄列表。這比僅僅設(shè)置queryset 屬性更靈活,就像我們在前面的代碼片段中所做的那樣(盡管在這種情況下沒有真正的好處): 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() ,以將其他上下文變量傳遞給模板(例如,默認情況下傳遞書本列表)。下面的片段,顯示了如何將一個名為“some_data ”的變量添加到上下文中(然后它將作為一個模板變量,而被提供)。 class BookListView(generic.ListView):
model = Book
def get_context_data(self, **kwargs):
# Call the base implementation first to get the context
context = super(BookListView, self).get_context_data(**kwargs)
# Create any data and add it to the context
context['some_data'] = 'This is just some data'
return context
這樣做時,遵循上面使用的模式非常重要: 創(chuàng)建列表視圖模板節(jié)創(chuàng)建 HTML 文件 /locallibrary/catalog/templates/catalog/book_list.html,并復制到下面的文本中。如上所述,這是基于類的通用列表視圖,所期望的默認模板文件(對于名為catalog 的應用程序中,名為Book 的模型)。 通用視圖的模板就像任何其他模板一樣(當然,傳遞給模板的上下文/信息可能不同)。與我們的索引模板一樣,我們在第一行擴展基本模板,然后替換名為content 的區(qū)塊。 {% 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 %}
視圖默認將上下文(書本列表)作為 object_list 和 book_list 的別名傳遞;任何一個都會奏效。 條件執(zhí)行我們使用 if , else 和 endif 模板標簽,來檢查 book_list 是否已定義且不為空。如果 book_list 為空,則 else 子句顯示文本,說明沒有要列出的書本。如果 book_list 不為空,那么我們遍歷書本列表。 {% if book_list %}
<!-- code here to list the books -->
{% else %}
<p>There are no books in the library.</p>
{% endif %}
上述條件僅檢查一種情況,但您可以使用 elif 模板標記(例如{% elif var2 %} )測試其他條件。有關(guān)條件運算符的更多信息,請參閱:if, ifequal/ifnotequal,以及內(nèi)置模板標記和過濾器(Django Docs)中的 ifchanged 。 For 循環(huán)/回圈模板使用for 和 endfor 模板標簽,以循環(huán)遍歷書本列表,如下所示。每次迭代都會使用當前列表項的信息,填充書本模板變量book 。 {% for book in book_list %}
<li> <!-- code here get information from each book item --> </li>
{% endfor %}
雖然這里沒有使用,但在循環(huán)中,Django 還會創(chuàng)建其他可用于跟蹤迭代的變量。例如,您可以測試forloop.last 變量,以運行最后一次循環(huán)當中的條件處理代碼。 訪問變量循環(huán)內(nèi)的代碼,為每本書創(chuàng)建一個列表項,顯示作者和標題(作為尚未創(chuàng)建的詳細視圖的鏈接)。 <a href="{{ book.get_absolute_url }}">{{ book.title }}</a> ({{book.author}})
我們使用“點符號”(例如 book.title 和 book.author )訪問相關(guān)書本記錄的字段,其中書本項目book 后面的文本是字段名稱(如同在模型中定義的)。 我們還可以在模板中,調(diào)用模型中的函數(shù) - 在這里,我們調(diào)用Book.get_absolute_url() ,來獲取可用于顯示關(guān)聯(lián)詳細記錄的URL。這項工作提供的函數(shù)沒有任何參數(shù)(沒有辦法傳遞參數(shù)?。?/p> 注意: 在模板中調(diào)用函數(shù)時,我們必須要小心“副作用”。在這里我們只需要顯示一個URL,但是一個函數(shù)幾乎可以做任何事情 - 我們不想僅僅通過渲染模板,而刪除了我們的數(shù)據(jù)庫(例如)! 更新基本模板打開基本模板(/locallibrary/catalog/templates/base_generic.html)并將 {% url 'books' %} 插入所有書本 All books 的 URL 鏈接,如下所示。這將啟用所有頁面中的鏈接(由于我們已經(jīng)創(chuàng)建了 “books” 的 url 映射器,我們可以成功地將其設(shè)置到位)。 <li><a href="{% url 'index' %}">Home</a></li><li><a href="{% url 'books' %}">All books</a></li><li><a href="">All authors</a></li>
您將無法構(gòu)建書本清單,因為我們?nèi)匀蝗鄙僖蕾図?- 書本詳細信息頁面的URL地圖,這是創(chuàng)建單個書本的超鏈接所必需的。我們將在下一節(jié)之后,說明列表和詳細視圖的部分。 書本詳細信息頁面,將顯示有關(guān)特定書本的信息,使用 URL catalog/book/<id> (其中 <id> 是書本的主鍵)進行訪問。除了Book 模型中的字段(作者,摘要,ISBN,語言和種類)之外,我們還將列出可用副本(BookInstances )的詳細信息,包括狀態(tài),預期返回日期,印記和 id。這將使我們的讀者,不僅可以了解該書,還可以確認是否/何時可用。 打開 /catalog/urls.py ,并添加下面粗體顯示的 “book-detail” URL 映射器。這個 path() 函數(shù)定義了一個模式,關(guān)聯(lián)到基于通用類的詳細信息視圖和名稱。 urlpatterns = [
path('', views.index, name='index'),
path('books/', views.BookListView.as_view(), name='books'), path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail'),]
對于書本詳細信息路徑,URL 模式使用特殊語法,來捕獲我們想要查看的書本的特定 id。語法非常簡單:尖括號定義要捕獲的URL部分,包含視圖可用于訪問捕獲數(shù)據(jù)的變量的名稱。例如,<something> 將捕獲標記的模式,并將值作為變量 “something” ,傳遞給視圖。您可以選擇在變量名稱前,加上一個定義數(shù)據(jù)類型的轉(zhuǎn)換器規(guī)范(int,str,slug,uuid,path)。 在這里,我們使用 '<int:pk>' 來捕獲 book id,它必須是一個整數(shù),并將其作為名為 pk 的參數(shù)(主鍵的縮寫)傳遞給視圖。 注意: 如前所述,我們匹配的URL實際上是 catalog/book/<digits> (因為我們在應用程序 catalog 中,假定使用/catalog/ )。 要點: 基于類的通用詳細信息視圖,需要傳遞一個名為 pk 的參數(shù)。如果您正在編寫自己的函數(shù)視圖,則可以使用您喜歡的任何參數(shù)名稱,或者,確實也可以,在未命名的參數(shù)中傳遞信息。 高級路徑匹配/正則表達式入門注意: 完成教程并不需要此部分說明!我們提供它,是因為了解此可選的部分,未來可能對您使用 Django 有幫助。 path() 提供的模式匹配非常簡單,對于您只想捕獲任何字符串或整數(shù)的(非常常見的)情況非常有用。如果需要更精細的過濾(例如,僅過濾具有一定數(shù)量字符的字符串),則可以使用 re_path() 方法。
此方法與 path() 的使用一樣,除了它允許您使用正則表達式,以指定模式。例如,上面的路徑可以編寫為如下所示:
re_path(r'^book/(?P<pk>\d+)$', views.BookDetailView.as_view(), name='book-detail'),
正則表達式是一種非常強大的模式映射工具。坦率地說,對于初學者來說,他們是非常不直觀和可怕的。下面是一個非常短的入門! 首先要知道的是,正則表達式通常應該使用原始字符串文字語法聲明(即它們?nèi)鐖D所示:r'<你的正則表達式文本放在這里>')。 聲明模式匹配需要知道的語法,主要部分是: 符號 | 含義 |
---|
^ | 匹配文本的開頭 | $ | 匹配文本的結(jié)尾 | \d | 匹配一個位數(shù)的數(shù)字(0,1,2,... 9) | \w | 匹配單詞字符,例如字母,數(shù)字或下劃線字符(_)中的任何大寫或小寫字符 | + | 匹配前面一個或多個字符。例如,要匹配一個或多個位數(shù)的數(shù)字,您將使用\d+ 。要匹配一個或多個“ a” 字符,您可以使用 a+ | * | 匹配前面字符的零個或多個。例如,要匹配沒有內(nèi)容或單詞,您可以使用\w* | ( ) | 捕獲括號內(nèi)部模式的一部分。任何捕獲的值,都將作為未命名參數(shù),傳遞給視圖(如果捕獲了多個模式,則將按照聲明捕獲的順序,提供相關(guān)參數(shù))。 | (?P<name>...) | 捕獲模式(由...表示)作為命名變量(在本例中為“name”)。捕獲的值,將傳遞給具有指定名稱的視圖。因此,您的視圖,必須聲明具有相同名稱的參數(shù)! | [ ] | 匹配集合中的一個字符。例如,[abc] 將匹配 'a' 或 'b' 或 'c'。 [-\w] 將匹配 ' - ' 字符,或任何單詞字符。 |
大多數(shù)其他字符可以按字面意思理解! 讓我們考慮一些模式的真實例子: 模式 | 描述 |
---|
r'^book/(?P<pk>\d+)$' | 這是我們的 url 映射器中使用的 RE。它匹配一個字符串,該字符串在行(^book/)的開頭具有book/ ,然后有一個或多個數(shù)字(\d+ ),然后結(jié)束(在行標記結(jié)束之前,沒有非數(shù)字字符)。 它還捕獲所有數(shù)字(?P<pk>\d+),并將它們傳遞給名為 'pk' 的參數(shù)中的視圖。捕獲的值始終作為字符串傳遞! 例如,這將匹配 book/1234 ,并向視圖發(fā)送變量 pk='1234' 。 | r'^book/(\d+)$' | 這與前面的例子匹配相同的URL。捕獲的信息,將作為未命名的參數(shù),發(fā)送到視圖。 | r'^book/(?P<stub>[-\w]+)$' | 這匹配一個字符串,該字符串在行(^book/)的開頭具有book/ ,然后有一個或多個字符,可以是 ' - ' 或單詞字符(([-\w]+),然后結(jié)束。它還捕獲這組字符,并將它們傳遞給名為 “stub” 的參數(shù)中的視圖。 這是 “stub” 的一種相當?shù)湫偷哪J?。存根stub 是用于數(shù)據(jù)的、 URL 友好的、基于單詞的主鍵。如果您希望本書網(wǎng)址提供更多信息,則可以使用 stub。例如 /catalog/book/the-secret-garden ,而不是/catalog/book/33 。 |
您可以在一個匹配中捕獲多個模式,從而在 URL 中,編碼許多不同的信息。 注意: 作為一項挑戰(zhàn),請考慮如何對網(wǎng)址進行編碼,以列出特定年份,月份,日期的所有圖書,以及可用于匹配它的規(guī)則表達式 RE。 在 URL 地圖中傳遞其他選項我們在這里沒有使用、但您可能覺得有價值的一個功能是,您可以向視圖聲明并傳遞其他選項。這些選項被聲明為一個字典,您將其作為第三個未命名參數(shù),傳遞給 path() 函數(shù)。 如果要對多個資源,使用相同的視圖,并在每種情況下,傳遞數(shù)據(jù)以配置其行為,則此方法非常有用(下面我們在每種情況下提供不同的模板)。 path('url/', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
path('anotherurl/', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),
注意: 額外選項和命名捕獲的模式,二者都作為命名參數(shù)傳遞給視圖。如果對捕獲的模式和額外選項使用相同的名稱,則僅將捕獲的模式值發(fā)送到視圖(將刪除附加選項中指定的值)。 打開 catalog / views.py,并將以下代碼復制到文件的底部: class BookDetailView(generic.DetailView):
model = Book
就是這樣!您現(xiàn)在需要做的就是創(chuàng)建一個名為 /locallibrary/catalog/templates/catalog/book_detail.html 的模板,該視圖將向此模板,傳遞 URL 映射器提取的特定 Book 記錄的數(shù)據(jù)庫信息。在模板中,您可以使用名為 object 或 book 的模板變量(即通常為 “the_model_name ”),以訪問書本列表。 如果需要,可以更改使用的模板,以及用于在模板中,引用該書本的上下文對象的名稱。您還可以覆蓋方法,例如,向上下文添加其他信息。 如果記錄不存在會怎樣?如果請求的記錄不存在,那么基于類的通用詳細信息視圖,將自動為您引發(fā) Http404 異常 - 在生產(chǎn)環(huán)境中,這將自動顯示適當?shù)?“未找到資源” 頁面,您可以根據(jù)需要自定義該頁面。 為了讓您了解其工作原理,下面的代碼片段,演示了如何在不使用基于類的詳細信息視圖的情況下,將基于類的視圖實現(xiàn)為函數(shù)。 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,}
)
視圖首先嘗試從模型中,獲取特定的書本記錄。如果失敗,則視圖應引發(fā) Http404 異常,以指示該書本 “未找到”。然后,最后一步是使用模板名稱,和上下文參數(shù)context 中的書本數(shù)據(jù)(作為字典)調(diào)用render() 。 注意: get_object_or_404() (如上所示)是一個方便的快捷方式,用于在未找到記錄時,引發(fā) Http404 異常。 創(chuàng)建詳細信息視圖模板節(jié)創(chuàng)建 HTML 文件 /locallibrary/catalog/templates/catalog/book_detail.html,并為其提供以下內(nèi)容。如上所述,這是基于類的通用詳細信息視圖,所期望的默認模板文件名(對于名為 catalog 的應用程序中名為 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 == 'm' %}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 %}
注意: 上面模板中的作者鏈接,有一個空 URL,因為我們尚未創(chuàng)建作者詳細信息頁面。一旦創(chuàng)建了,您應該像這樣更新URL: <a href="{% url 'author-detail' book.author.pk %}">{{ book.author }}</a>
雖然有點大,但此模板中的幾乎所有內(nèi)容,都已在前面描述過: 我們擴展基本模板,并覆蓋 “內(nèi)容”區(qū)塊 content。 我們使用條件處理,來確定是否顯示特定內(nèi)容。 我們使用 for 循環(huán)遍歷對象列表。 我們使用 "點表示法" 訪問上下文字段(因為我們使用了詳細的通用視圖,上下文被命名為book ;我們也可以使用 “object ”)。
我們以前沒見過的一件有趣的事情是函數(shù)book.bookinstance_set.all() 。此方法由 Django “自動” 構(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 %}
需要此方法,是因為您僅在關(guān)系的 “一” 側(cè)聲明 ForeignKey (一對多)字段。由于您沒有做任何事情,來聲明其他(“多”)模型中的關(guān)系,因此它沒有任何字段,來獲取相關(guān)記錄集。為了解決這個問題,Django構(gòu)造了一個適當命名的 “反向查找” 函數(shù),您可以使用它。函數(shù)的名稱,是通過對聲明 ForeignKey 的模型名稱,轉(zhuǎn)化為小寫來構(gòu)造的,然后是_set (即,在 Book 中創(chuàng)建的函數(shù)是 bookinstance_set() )。 注意: 這里我們使用all() 來獲取所有記錄(默認值)。雖然您可以使用filter() 方法獲取代碼中的記錄子集,但您無法直接在模板中執(zhí)行此操作,因為您無法指定函數(shù)的參數(shù)。 還要注意,如果您沒有定義順序(在基于類的視圖或模型上),您還會看到開發(fā)服務器中的錯誤,如下所示: [29/May/2017 18:37:53] "GET /catalog/books/?page=1 HTTP/1.1" 200 1637
/foo/local_library/venv/lib/python3.5/site-packages/django/views/generic/list.py:99: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <QuerySet [<Author: Ortiz, David>, <Author: H. McRaven, William>, <Author: Leigh, Melinda>]>
allow_empty_first_page=allow_empty_first_page, **kwargs)
發(fā)生這種情況,是因為 paginator object 對象希望在下劃線數(shù)據(jù)庫上看到一些 ORDER BY。沒有它,它無法確定,返回的注冊表實際上是否為正確順序! 本教程還沒有說明到 Pagination(還沒,但很快),但由于你不能使用sort_by() 并傳遞一個參數(shù)(與上面描述的filter() 相同),你將不得不在下面三個選擇當中,進行挑選: 在模型的class Meta 聲明中,添加排序ordering 。 Add a queryset attribute in your custom class-based view, specifying a order_by() .在自定義基于類的視圖中添加queryset屬性,指定order_by()。 Adding a get_queryset method to your custom class-based view and also specify the order_by() .將get_queryset方法添加到基于類的自定義視圖中,并指定order_by()。
如果您決定使用class Meta 作為作者模型Author (可能不像定制基于類的視圖那樣靈活,但很容易),您最終會得到這樣的結(jié)果: class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return '%s, %s' % (self.last_name, self.first_name)
class Meta:
ordering = ['last_name']
當然,該字段不需要是last_name :它可以是任何其他字段。 最后,但并非最不重要的是,您應該按照實際上在數(shù)據(jù)庫上具有索引(唯一或非唯一)的屬性/欄位進行排序,以避免性能問題。當然,如果這么少量的書本(和用戶?。@里就沒有必要(我們可能會讓自己提前做太多事情),但是對于未來的項目來說,這是需要考慮的事情。 此時,我們應該創(chuàng)建了顯示書本列表,和書本詳細信息頁面所需的所有內(nèi)容。運行服務器(python3 manage.py runserver ),并打開瀏覽器到 http://127.0.0.1:8000/。 警告: 請還不要點擊任何作者、或作者詳細信息鏈接 - 您將在挑戰(zhàn)練習中,創(chuàng)建這些鏈接! 單擊所有書籍鏈接 All books ,以顯示書籍列表。 
然后點擊指向您的某本圖書的鏈接。如果一切設(shè)置正確,您應該看到類似下面的屏幕截圖。 
如果您剛剛獲得了一些記錄,我們的圖書清單頁面看起來會很好。但是,當您進入數(shù)十或數(shù)百條記錄的頁面時,頁面將逐漸花費更長時間加載(并且有太多內(nèi)容無法合理瀏覽)。此問題的解決方案,是為列表視圖添加分頁,減少每頁上顯示的項目數(shù)。 Django 在分頁方面,擁有出色的內(nèi)置支持。更好的是,它內(nèi)置于基于類的通用列表視圖中,因此您無需執(zhí)行太多操作即可啟用它! 打開 catalog/views.py,然后添加下面粗體顯示的paginate_by 行。 class BookListView(generic.ListView):
model = Bookpaginate_by = 10
通過添加這行,只要您有超過10條記錄,視圖就會開始對它發(fā)送到模板的數(shù)據(jù),進行分頁。使用 GET 參數(shù)訪問不同的頁面 - 要訪問第2頁,您將使用URL:/catalog/books/?page=2 。 現(xiàn)在數(shù)據(jù)已經(jīng)分頁,我們需要添加對模板的支持,以滾動結(jié)果集合。因為我們可能希望在所有列表視圖中,都執(zhí)行此操作,所以我們將以可添加到基本模板的方式,執(zhí)行此操作。 打開 /locallibrary/catalog/templates/base_generic.html,并復制貼士以下內(nèi)容區(qū)塊下面的分頁區(qū)塊(以粗體突出顯示)。代碼首先檢查當前頁面上,是否啟用了分頁。如果是,則它會根據(jù)需要,添加下一個和上一個鏈接(以及當前頁碼)。 {% 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 對象,如果在當前頁面上使用分頁,它將存在。 它允許您獲取有關(guān)當前頁面,之前頁面,有多少頁面等的所有信息。
我們使用 {{ request.path }} ,來獲取用于創(chuàng)建分頁鏈接的當前頁面URL。 這很有用,因為它獨立于我們正在分頁的對象。 就是這樣! 下面的屏幕截圖,顯示了分頁的樣子 - 如果您沒有在數(shù)據(jù)庫中輸入超過10個標題,那么您可以通過降低 catalog/views.py 文件中 paginate_by 行指定的數(shù)量,來更輕松地測試它。 為了得到以下結(jié)果,我們將其更改為 paginate_by = 2 。 分頁鏈接顯示在底部,根據(jù)您所在的頁面,顯示下一個/上一個鏈接。 
本文中的挑戰(zhàn),是創(chuàng)建完成項目所需的作者詳細信息視圖,和列表視圖。這些應在以下URL中提供: URL 映射器和視圖所需的代碼,應與我們上面創(chuàng)建的Book 列表和詳細視圖幾乎完全相同。模板將有所不同,但會分享類似的行為。 注意: 為作者列表頁面,創(chuàng)建URL映射器之后,還需要更新基本模板中的所有作者 All authors 鏈接。按照我們更新“所有圖書”All books 鏈接時,所做的相同過程。 為作者詳細信息頁面,創(chuàng)建URL映射器之后,還應更新書本詳細信息視圖模板(/locallibrary/catalog/templates/catalog/book_detail.html),以便作者鏈接,指向新的作者詳細信息頁面(而不是一個空的URL)。該行將更改為添加下面以粗體顯示的模板標記。 <p><strong>Author:</strong> <a href="{% url 'author-detail' book.author.pk %}">{{ book.author }}</a></p>
完成后,您的頁面應該類似于下面的屏幕截圖。 

恭喜,我們的圖書館的基本功能現(xiàn)在完成了! 本文中,我們學到如何使用基于類別的通用列表視圖與詳細視圖,并使用它們創(chuàng)建頁面,以查看我們的書本和作者。在此過程中,我們了解了與正則表達式匹配的模式,以及如何將數(shù)據(jù)從URL傳遞到視圖。我們還學習了一些使用模板的技巧。最后,我們已經(jīng)展示了如何對列表視圖進行分頁,這樣即使我們有很多記錄,我們也可以管理列表。 在我們的下一篇文章,我們將擴充此圖書館,以支持使用者帳戶,并從而演示使用者授權(quán)、許可、授權(quán)、會話, 以及表單。
|