aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/learn/server-side/django/generic_views
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/learn/server-side/django/generic_views
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/learn/server-side/django/generic_views')
-rw-r--r--files/zh-cn/learn/server-side/django/generic_views/index.html630
1 files changed, 630 insertions, 0 deletions
diff --git a/files/zh-cn/learn/server-side/django/generic_views/index.html b/files/zh-cn/learn/server-side/django/generic_views/index.html
new file mode 100644
index 0000000000..c8eeefc366
--- /dev/null
+++ b/files/zh-cn/learn/server-side/django/generic_views/index.html
@@ -0,0 +1,630 @@
+---
+title: 'Django 教程 6: 通用列表和详细信息视图'
+slug: learn/Server-side/Django/Generic_views
+translation_of: Learn/Server-side/Django/Generic_views
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">本教程扩充了 <a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 网站,为书本与作者增加列表与细节页面。此处我们将学到通用类别视图,并演示如何降低你必须为一般使用案例撰写的程式码数量。我们也会更加深入URL处理细节,演示如何实施基本模式匹配。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>完成所有先前的教程主题,包含<a href="/zh-CN/docs/Learn/Server-side/Django/Home_page">Django 教程 5: 创建主页。</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解如何使用、在何处使用通用类别视图,以及如何从URLs取出模式,如何传送资料到视图。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="概览">概览</h2>
+
+<p>本教程中,通过为书本和作者添加列表和详细信息页面,我们将完成第一个版本的<a href="/zh-CN/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> 网站(或者更准确地说,我们将向您展示如何实现书页,并让您自己创建作者页面!)</p>
+
+<p>该过程类似于创建索引页面,我们在上一个教程中展示了该页面。我们仍然需要创建URL地图,视图和模板。主要区别在于,对于详细信息页面,我们还有一个额外的挑战,即从URL中的模式中提取信息,并将其传递给视图。对于这些页面,我们将演示一种完全不同的视图类型:基于类别的通用列表和详细视图。这些可以显着减少所需的视图代码量,使其更易于编写和维护。</p>
+
+<p>本教程的最后一部分,将演示在使用基于类别的通用列表视图时,如何对数据进行分页。</p>
+
+<h2 id="书本清单页面">书本清单页面</h2>
+
+<p>书本清单页面,将显示页面中所有可用图书记录的列表,使用url: <code>catalog/books/</code>进行访问。该页面将显示每条记录的标题和作者,标题是指向相关图书详细信息页面的超链接。该页面将具有与站点中,所有其他页面相同的结构和导航,因此,我们可以扩展在上一个教程中创建的基本模板(<strong>base_generic.html</strong>)。</p>
+
+<h3 id="URL_映射">URL 映射</h3>
+
+<p>打开<strong>/catalog/urls.py</strong> ,并复制到下面粗体显示的行中。就像索引页面的方式,这个<code>path()</code>函数,定义了一个与 URL 匹配的模式('<strong>books/</strong>'),如果URL匹配,将调用视图函数(<code>views.BookListView.as_view()</code>)和一个对应这个特定映射的名称。</p>
+
+<pre class="brush: python">urlpatterns = [
+ path('', views.index, name='index'),
+<strong>  </strong>path<strong>('books/', views.BookListView.as_view(), name='books'),</strong>
+]</pre>
+
+<p>正如前一个教程中所讨论的,URL 必须已经先匹配了<code>/catalog</code>,因此实际上将为 URL 调用的视图是:<code>/catalog/books/</code>。</p>
+
+<p>视图函数具有与以前不同的格式 - 这是因为该视图,实际上将以类别来实现。我们将继承现有的泛型视图函数,该函数已经完成了我们希望此视图函数执行的大部分工作,而不是从头开始编写自己的函数。对于基于Django类的视图,我们通过调用类方法<code>as_view()</code>,来访问适当的视图函数。这样做可以创建类的实例,并确保为传入的 HTTP 请求调用正确的处理程序方法。</p>
+
+<h3 id="视图_(基于类别)">视图 (基于类别)</h3>
+
+<p>我们可以很容易地,将书本列表视图编写为常规函数(就像我们之前的索引视图一样),它将查询数据库中的所有书本,然后调用<code>render()</code>,将列表传递给指定的模板。然而,我们用另一种方法取代,我们将使用基于类的通用列表视图(<code>ListView</code>) - 一个继承自现有视图的类。因为通用视图,已经实现了我们需要的大部分功能,并且遵循 Django 最佳实践,我们将能够创建更强大的列表视图,代码更少,重复次数更少,最终维护更少。</p>
+
+<p>打开<strong> catalog/views.py</strong>,并将以下代码复制到文件的底部:</p>
+
+<pre class="brush: python">from django.views import generic
+
+class BookListView(generic.ListView):
+ model = Book</pre>
+
+<p>就是这样!通用视图将查询数据库,以获取指定模型(<code>Book</code>)的所有记录,然后呈现位于<strong>/locallibrary/catalog/templates/catalog/book_list.html</strong> 的模板(我们将在下面创建)。在模板中,您可以使用名为<code>object_list</code> 或 <code>book_list</code>的模板变量(即通常为“<code><em>the_model_name</em>_list</code>”),以访问书本列表。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 模板位置的这个尴尬路径不是印刷错误 - 通用视图在应用程序的<code>/<em>application_name</em>/templates/</code>目录中<code>(/catalog/templates/</code>),查找模板<code>/<em>application_name</em>/<em>the_model_name</em>_list.html</code>(在本例中为<code>catalog/book_list.html</code>)。</p>
+</div>
+
+<p>您可以添加属性,以更改上面的默认行为。例如,如果需要使用同一模型的多个视图,则可以指定另一个模板文件,或者如果<code>book_list</code>对于特定模板用例不直观,则可能需要使用不同的模板变量名称。可能最有用的变更,是更改/过滤返回的结果子集 - 因此,您可能会列出其他用户阅读的前5本书,而不是列出所有书本。</p>
+
+<pre class="brush: python">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</pre>
+
+<h4 id="覆盖基于类别的视图中的方法">覆盖基于类别的视图中的方法</h4>
+
+<p>虽然我们不需要在这里执行此操作,但您也可以覆盖某些类别方法。</p>
+
+<p>例如,我们可以覆盖<code>get_queryset()</code>方法,来更改返回的记录列表。这比仅仅设置<code>queryset</code>属性更灵活,就像我们在前面的代码片段中所做的那样(尽管在这种情况下没有真正的好处):</p>
+
+<pre class="brush: python">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
+</pre>
+
+<p>我们还可以覆盖<code>get_context_data()</code> ,以将其他上下文变量传递给模板(例如,默认情况下传递书本列表)。下面的片段,显示了如何将一个名为“<code>some_data</code>”的变量添加到上下文中(然后它将作为一个模板变量,而被提供)。</p>
+
+<pre class="brush: python">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</pre>
+
+<p>这样做时,遵循上面使用的模式非常重要:</p>
+
+<ul>
+ <li>首先从我们的超类别中,获取现有的上下文。</li>
+ <li>然后添加新的上下文信息。</li>
+ <li>然后返回新的(更新的)上下文。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: 查看<a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/">内置的基于类的通用视图</a>(Django文档),了解更多可以执行的操作示例。</p>
+</div>
+
+<h3 id="创建列表视图模板">创建列表视图模板</h3>
+
+<p>创建 HTML 文件 <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong>,并复制到下面的文本中。如上所述,这是基于类的通用列表视图,所期望的默认模板文件(对于名为<code>catalog</code>的应用程序中,名为<code>Book</code>的模型)。</p>
+
+<p>通用视图的模板就像任何其他模板一样(当然,传递给模板的上下文/信息可能不同)。与我们的索引模板一样,我们在第一行扩展基本模板,然后替换名为<code>content</code>的区块。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+    &lt;h1&gt;Book List&lt;/h1&gt;
+
+    <strong>{% if book_list %}</strong>
+    &lt;ul&gt;
+
+      {% for book in book_list %}
+      &lt;li&gt;
+        &lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+      &lt;/li&gt;
+      {% endfor %}
+
+    &lt;/ul&gt;
+    <strong>{% else %}</strong>
+      &lt;p&gt;There are no books in the library.&lt;/p&gt;
+    <strong>{% endif %} </strong>
+{% endblock %}</pre>
+
+<p>视图默认将上下文(书本列表)作为 <code>object_list </code>和 <code>book_list</code> 的别名传递;任何一个都会奏效。</p>
+
+<h4 id="条件执行">条件执行</h4>
+
+<p>我们使用 <code><a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#if">if</a></code>, <code>else</code> 和 <code>endif</code>模板标签,来检查 <code>book_list</code>是否已定义且不为空。如果 <code>book_list</code>为空,则 <code>else</code>子句显示文本,说明没有要列出的书本。如果 <code>book_list</code>不为空,那么我们遍历书本列表。</p>
+
+<pre class="brush: html"><strong>{% if book_list %}</strong>
+ &lt;!-- code here to list the books --&gt;
+<strong>{% else %}</strong>
+ &lt;p&gt;There are no books in the library.&lt;/p&gt;
+<strong>{% endif %}</strong>
+</pre>
+
+<p>上述条件仅检查一种情况,但您可以使用 <code>elif </code>模板标记(例如<code>{% elif var2 %}</code> )测试其他条件。有关条件运算符的更多信息,请参阅:<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#if">if</a>, <a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#ifequal-and-ifnotequal">ifequal/ifnotequal</a>,以及<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins">内置模板标记和过滤器</a>(Django Docs)中的 <a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#ifchanged">ifchanged</a> 。</p>
+
+<h4 id="For_循环回圈">For 循环/回圈</h4>
+
+<p>模板使用<a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#for">for</a> 和 <code>endfor</code>模板标签,以循环遍历书本列表,如下所示。每次迭代都会使用当前列表项的信息,填充书本模板变量<code>book</code>。</p>
+
+<pre class="brush: html">{% for <strong>book</strong> in book_list %}
+ &lt;li&gt; &lt;!-- code here get information from each <strong>book</strong> item --&gt; &lt;/li&gt;
+{% endfor %}
+</pre>
+
+<p>虽然这里没有使用,但在循环中,Django 还会创建其他可用于跟踪迭代的变量。例如,您可以测试<code>forloop.last</code> 变量,以运行最后一次循环当中的条件处理代码。</p>
+
+<h4 id="访问变量">访问变量</h4>
+
+<p>循环内的代码,为每本书创建一个列表项,显示作者和标题(作为尚未创建的详细视图的链接)。</p>
+
+<pre class="brush: html">&lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+</pre>
+
+<p>我们使用“点符号”(例如 <code>book.title</code> 和 <code>book.author</code>)访问相关书本记录的字段,其中书本项目<code>book</code>后面的文本是字段名称(如同在模型中定义的)。</p>
+
+<p>我们还可以在模板中,调用模型中的函数 - 在这里,我们调用<code>Book.get_absolute_url()</code>,来获取可用于显示关联详细记录的URL。这项工作提供的函数没有任何参数(没有办法传递参数!)</p>
+
+<div class="note">
+<p><strong>注意</strong>: 在模板中调用函数时,我们必须要小心“副作用”。在这里我们只需要显示一个URL,但是一个函数几乎可以做任何事情 - 我们不想仅仅通过渲染模板,而删除了我们的数据库(例如)!</p>
+</div>
+
+<h4 id="更新基本模板">更新基本模板</h4>
+
+<p>打开基本模板(<strong>/locallibrary/catalog/templates/base_generic.html</strong>)并将 <strong>{% url 'books' %} </strong>插入所有书本 <strong>All books </strong>的 URL 链接,如下所示。这将启用所有页面中的链接(由于我们已经创建了 “books” 的 url 映射器,我们可以成功地将其设置到位)。</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+<strong>&lt;li&gt;&lt;a href="{% url 'books' %}"&gt;All books&lt;/a&gt;&lt;/li&gt;</strong>
+&lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;</pre>
+
+<h3 id="它看起来是什么样子?">它看起来是什么样子?</h3>
+
+<p>您将无法构建书本清单,因为我们仍然缺少依赖项 - 书本详细信息页面的URL地图,这是创建单个书本的超链接所必需的。我们将在下一节之后,说明列表和详细视图的部分。</p>
+
+<h2 id="书本详细信息页面">书本详细信息页面</h2>
+
+<p>书本详细信息页面,将显示有关特定书本的信息,使用 URL <code>catalog/book/<em>&lt;id&gt;</em></code>(其中 <code><em>&lt;id&gt; </em></code>是书本的主键)进行访问。除了<code>Book</code>模型中的字段(作者,摘要,ISBN,语言和种类)之外,我们还将列出可用副本(<code>BookInstances</code>)的详细信息,包括状态,预期返回日期,印记和 id。这将使我们的读者,不仅可以了解该书,还可以确认是否/何时可用。</p>
+
+<h3 id="URL_映射_2">URL 映射</h3>
+
+<p>打开 <strong>/catalog/urls.py</strong> ,并添加下面粗体显示的 <strong>“book-detail”</strong> URL 映射器。这个<code> path()</code> 函数定义了一个模式,关联到基于通用类的详细信息视图和名称。</p>
+
+<pre class="brush: python">urlpatterns = [
+ path('', views.index, name='index'),
+    path('books/', views.BookListView.as_view(), name='books'),
+<strong>  path('book/&lt;int:pk&gt;', views.BookDetailView.as_view(), name='book-detail'),</strong>
+]</pre>
+
+<p>对于书本详细信息路径,URL 模式使用特殊语法,来捕获我们想要查看的书本的特定 id。语法非常简单:尖括号定义要捕获的URL部分,包含视图可用于访问捕获数据的变量的名称。例如,&lt;<strong>something</strong>&gt; 将捕获标记的模式,并将值作为变量 “something” ,传递给视图。您可以选择在变量名称前,加上一个定义数据类型的<a href="https://docs.djangoproject.com/en/2.0/topics/http/urls/#path-converters">转换器规范</a>(int,str,slug,uuid,path)。</p>
+
+<p>在这里,我们使用 <code>'&lt;int:pk&gt;' </code>来捕获 book id,它必须是一个整数,并将其作为名为 <code>pk </code>的参数(主键的缩写)传递给视图。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 如前所述,我们匹配的URL实际上是 <code>catalog/book/&lt;digits&gt;</code>(因为我们在应用程序 <strong>catalog </strong>中,假定使用<code>/catalog/</code>)。</p>
+</div>
+
+<div class="warning">
+<p><strong>要点</strong>: 基于类的通用详细信息视图,需要传递一个名为 <strong>pk </strong>的参数。如果您正在编写自己的函数视图,则可以使用您喜欢的任何参数名称,或者,确实也可以,在未命名的参数中传递信息。</p>
+</div>
+
+<h4 id="高级路径匹配正则表达式入门">高级路径匹配/正则表达式入门</h4>
+
+<div class="note">
+<p><strong>注意</strong>: 完成教程并不需要此部分说明!我们提供它,是因为了解此可选的部分,未来可能对您使用 Django 有帮助。</p>
+</div>
+
+<p><code>path()</code>提供的模式匹配非常简单,对于您只想捕获任何字符串或整数的(非常常见的)情况非常有用。如果需要更精细的过滤(例如,仅过滤具有一定数量字符的字符串),则可以使用 <a href="https://docs.djangoproject.com/en/2.0/ref/urls/#django.urls.re_path">re_path()</a> 方法。</p>
+
+<p>此方法与</p>
+
+<p><code>path()</code>的使用一样,除了它允许您使用<a href="https://docs.python.org/3/library/re.html">正则表达式</a>,以指定模式。例如,上面的路径可以编写为如下所示:</p>
+
+<pre class="brush: python"><strong>re_path(r'^book/(?P&lt;pk&gt;\d+)$', views.BookDetailView.as_view(), name='book-detail'),</strong>
+</pre>
+
+<p>正则表达式是一种非常强大的模式映射工具。坦率地说,对于初学者来说,他们是非常不直观和可怕的。下面是一个非常短的入门!</p>
+
+<p>首先要知道的是,正则表达式通常应该使用原始字符串文字语法声明(即它们如图所示:<strong>r'&lt;你的正则表达式文本放在这里&gt;'</strong>)。</p>
+
+<p>声明模式匹配需要知道的语法,主要部分是:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">符号</th>
+ <th scope="col">含义</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>^</td>
+ <td>匹配文本的开头</td>
+ </tr>
+ <tr>
+ <td>$</td>
+ <td>匹配文本的结尾</td>
+ </tr>
+ <tr>
+ <td>\d</td>
+ <td>匹配一个位数的数字(0,1,2,... 9)</td>
+ </tr>
+ <tr>
+ <td>\w</td>
+ <td>
+ <p>匹配单词字符,例如字母,数字或下划线字符(_)中的任何大写或小写字符</p>
+ </td>
+ </tr>
+ <tr>
+ <td>+</td>
+ <td>匹配前面一个或多个字符。例如,要匹配一个或多个位数的数字,您将使用<code>\d+</code>。要匹配一个或多个“ a” 字符,您可以使用 <code>a+</code></td>
+ </tr>
+ <tr>
+ <td>*</td>
+ <td>匹配前面字符的零个或多个。例如,要匹配没有内容或单词,您可以使用<code>\w*</code></td>
+ </tr>
+ <tr>
+ <td>( )</td>
+ <td>捕获括号内部模式的一部分。任何捕获的值,都将作为未命名参数,传递给视图(如果捕获了多个模式,则将按照声明捕获的顺序,提供相关参数)。</td>
+ </tr>
+ <tr>
+ <td>(?P&lt;<em>name</em>&gt;...)</td>
+ <td>捕获模式(由...表示)作为命名变量(在本例中为“name”)。捕获的值,将传递给具有指定名称的视图。因此,您的视图,必须声明具有相同名称的参数!</td>
+ </tr>
+ <tr>
+ <td>[  ]</td>
+ <td>匹配集合中的一个字符。例如,[abc] 将匹配 'a' 或 'b' 或 'c'。 [-\w] 将匹配 ' - ' 字符,或任何单词字符。</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>大多数其他字符可以按字面意思理解!</p>
+
+<p>让我们考虑一些模式的真实例子:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">模式</th>
+ <th scope="col">描述</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>r'^book/(?P&lt;pk&gt;\d+)$'</strong></td>
+ <td>
+ <p>这是我们的 url 映射器中使用的 RE。它匹配一个字符串,该字符串在行(<strong>^book/</strong>)的开头具有<code>book/</code>,然后有一个或多个数字(<code>\d+</code>),然后结束(在行标记结束之前,没有非数字字符)。</p>
+
+ <p>它还捕获所有数字(<strong>?P&lt;pk&gt;\d+</strong>),并将它们传递给名为 'pk' 的参数中的视图。<strong>捕获的值始终作为字符串传递</strong>!</p>
+
+ <p>例如,这将匹配 <code>book/1234</code>,并向视图发送变量 <code>pk='1234'</code>。</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(\d+)$'</strong></td>
+ <td>这与前面的例子匹配相同的URL。捕获的信息,将作为未命名的参数,发送到视图。</td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(?P&lt;stub&gt;[-\w]+)$'</strong></td>
+ <td>
+ <p>这匹配一个字符串,该字符串在行(<strong>^book/</strong>)的开头具有<code>book/</code>,然后有一个或多个字符,可以是 ' - ' 或单词字符((<strong>[-\w]+</strong>),然后结束。它还捕获这组字符,并将它们传递给名为 “stub” 的参数中的视图。</p>
+
+ <p>这是 “stub” 的一种相当典型的模式。存根stub 是用于数据的、 URL 友好的、基于单词的主键。如果您希望本书网址提供更多信息,则可以使用 stub。例如 <code>/catalog/book/the-secret-garden</code> ,而不是<code>/catalog/book/33</code>。</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>您可以在一个匹配中捕获多个模式,从而在 URL 中,编码许多不同的信息。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 作为一项挑战,请考虑如何对网址进行编码,以列出特定年份,月份,日期的所有图书,以及可用于匹配它的规则表达式 RE。</p>
+</div>
+
+<h4 id="在_URL_地图中传递其他选项">在 URL 地图中传递其他选项</h4>
+
+<p>我们在这里没有使用、但您可能觉得有价值的一个功能是,您可以向视图声明并传递<a href="https://docs.djangoproject.com/en/2.0/topics/http/urls/#views-extra-options">其他选项</a>。这些选项被声明为一个字典,您将其作为第三个未命名参数,传递给  <code>path()</code>函数。</p>
+
+<p>如果要对多个资源,使用相同的视图,并在每种情况下,传递数据以配置其行为,则此方法非常有用(下面我们在每种情况下提供不同的模板)。</p>
+
+<pre class="brush: python">path('url/', views.my_reused_view, <strong>{'my_template_name': 'some_path'}</strong>, name='aurl'),
+path('anotherurl/', views.my_reused_view, <strong>{'my_template_name': 'another_path'}</strong>, name='anotherurl'),
+</pre>
+
+<div class="note">
+<p><strong>注意:</strong> 额外选项和命名捕获的模式,二者都作为命名参数传递给视图。如果对捕获的模式和额外选项使用<strong>相同的名称</strong>,则仅将捕获的模式值发送到视图(将删除附加选项中指定的值)。</p>
+</div>
+
+<h3 id="视图_(基于类别)_2">视图 (基于类别)</h3>
+
+<p>打开 <strong>catalog / views.py</strong>,并将以下代码复制到文件的底部:</p>
+
+<pre class="brush: python">class BookDetailView(generic.DetailView):
+    model = Book</pre>
+
+<p>就是这样!您现在需要做的就是创建一个名为 <strong>/locallibrary/catalog/templates/catalog/book_detail.html </strong>的模板,该视图将向此模板,传递 URL 映射器提取的特定 <code>Book</code> 记录的数据库信息。在模板中,您可以使用名为  <code>object</code> 或 <code>book</code>的模板变量(即通常为 “<code><em>the_model_name</em></code>”),以访问书本列表。</p>
+
+<p>如果需要,可以更改使用的模板,以及用于在模板中,引用该书本的上下文对象的名称。您还可以覆盖方法,例如,向上下文添加其他信息。</p>
+
+<h4 id="如果记录不存在会怎样?">如果记录不存在会怎样?</h4>
+
+<p>如果请求的记录不存在,那么基于类的通用详细信息视图,将自动为您引发 <code>Http404 </code>异常 - 在生产环境中,这将自动显示适当的 “未找到资源” 页面,您可以根据需要自定义该页面。</p>
+
+<p>为了让您了解其工作原理,下面的代码片段,演示了如何在<strong>不使用</strong>基于类的详细信息视图的情况下,将基于类的视图实现为函数。</p>
+
+<pre class="brush: python">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,}
+ )
+</pre>
+
+<p>视图首先尝试从模型中,获取特定的书本记录。如果失败,则视图应引发 <code>Http404</code>异常,以指示该书本 “未找到”。然后,最后一步是使用模板名称,和上下文参数<code>context</code>中的书本数据(作为字典)调用<code>render()</code>。</p>
+
+<div class="note">
+<p><strong>注意</strong>: <code>get_object_or_404()</code>(如上所示)是一个方便的快捷方式,用于在未找到记录时,引发 <code>Http404 </code>异常。</p>
+</div>
+
+<h3 id="创建详细信息视图模板">创建详细信息视图模板</h3>
+
+<p>创建 HTML 文件 <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>,并为其提供以下内容。如上所述,这是基于类的通用详细信息视图,所期望的默认模板文件名(对于名为 <code>catalog </code>的应用程序中名为 <code>Book </code>的模型)。</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Title: \{{ book.title }}&lt;/h1&gt;
+
+ &lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href=""&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt; &lt;!-- author detail link not yet defined --&gt;
+ &lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; \{{ book.summary }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;ISBN:&lt;/strong&gt; \{{ book.isbn }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Language:&lt;/strong&gt; \{{ book.language }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Genre:&lt;/strong&gt; {% for genre in book.genre.all %} \{{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}&lt;/p&gt;
+
+ &lt;div style="margin-left:20px;margin-top:20px"&gt;
+ &lt;h4&gt;Copies&lt;/h4&gt;
+
+ {% for copy in book.bookinstance_set.all %}
+ &lt;hr&gt;
+ &lt;p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}"&gt;\{{ copy.get_status_display }}&lt;/p&gt;
+ {% if copy.status != 'a' %}&lt;p&gt;&lt;strong&gt;Due to be returned:&lt;/strong&gt; \{{copy.due_back}}&lt;/p&gt;{% endif %}
+ &lt;p&gt;&lt;strong&gt;Imprint:&lt;/strong&gt; \{{copy.imprint}}&lt;/p&gt;
+ &lt;p class="text-muted"&gt;&lt;strong&gt;Id:&lt;/strong&gt; \{{copy.id}}&lt;/p&gt;
+ {% endfor %}
+ &lt;/div&gt;
+{% endblock %}</pre>
+
+<ul>
+</ul>
+
+<div class="note">
+<p><strong>注意: </strong>上面模板中的作者链接,有一个空 URL,因为我们尚未创建作者详细信息页面。一旦创建了,您应该像这样更新URL:</p>
+
+<pre>&lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;
+</pre>
+</div>
+
+<p>虽然有点大,但此模板中的几乎所有内容,都已在前面描述过:</p>
+
+<ul>
+ <li>我们扩展基本模板,并覆盖 “内容”区块 content。</li>
+ <li>我们使用条件处理,来确定是否显示特定内容。</li>
+ <li>我们使用 <code>for </code>循环遍历对象列表。</li>
+ <li>我们使用 "点表示法" 访问上下文字段(因为我们使用了详细的通用视图,上下文被命名为<code>book</code>;我们也可以使用 “<code>object</code>”)。</li>
+</ul>
+
+<p>我们以前没见过的一件有趣的事情是函数<code>book.bookinstance_set.all()</code>。此方法由 Django “自动” 构造,以便返回与特定<code> Book</code> 相关联的 <code>BookInstance</code>记录集合。</p>
+
+<pre class="brush: python">{% for copy in book.bookinstance_set.all %}
+&lt;!-- code to iterate across each copy/instance of a book --&gt;
+{% endfor %}</pre>
+
+<p>需要此方法,是因为您仅在关系的 “一” 侧声明 <code>ForeignKey</code>(一对多)字段。由于您没有做任何事情,来声明其他(“多”)模型中的关系,因此它没有任何字段,来获取相关记录集。为了解决这个问题,Django构造了一个适当命名的 “反向查找” 函数,您可以使用它。函数的名称,是通过对声明<code> ForeignKey</code> 的模型名称,转化为小写来构造的,然后是<code>_set</code>(即,在 <code>Book </code>中创建的函数是 <code>bookinstance_set()</code>)。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 这里我们使用<code>all()</code>来获取所有记录(默认值)。虽然您可以使用<code>filter()</code>方法获取代码中的记录子集,但您无法直接在模板中执行此操作,因为您无法指定函数的参数。</p>
+
+<p>还要注意,如果您没有定义顺序(在基于类的视图或模型上),您还会看到开发服务器中的错误,如下所示:</p>
+
+<pre>[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: &lt;QuerySet [&lt;Author: Ortiz, David&gt;, &lt;Author: H. McRaven, William&gt;, &lt;Author: Leigh, Melinda&gt;]&gt;
+ allow_empty_first_page=allow_empty_first_page, **kwargs)
+</pre>
+
+<p>发生这种情况,是因为 <a href="https://docs.djangoproject.com/en/2.0/topics/pagination/#paginator-objects">paginator object </a>对象希望在下划线数据库上看到一些 ORDER BY。没有它,它无法确定,返回的注册表实际上是否为正确顺序!</p>
+
+<p>本教程还没有说明到 <strong>Pagination</strong>(还没,但很快),但由于你不能使用<code>sort_by()</code> 并传递一个参数(与上面描述的<code>filter()</code> 相同),你将不得不在下面三个选择当中,进行挑选:   </p>
+
+<ol>
+ <li>在模型的<code>class Meta</code>声明中,添加排序<code>ordering</code>。</li>
+ <li>Add a <code>queryset</code> attribute in your custom class-based view, specifying a <code>order_by()</code>.在自定义基于类的视图中添加queryset属性,指定order_by()。</li>
+ <li>Adding a <code>get_queryset</code> method to your custom class-based view and also specify the <code>order_by()</code>.将get_queryset方法添加到基于类的自定义视图中,并指定order_by()。</li>
+</ol>
+
+<p>如果您决定使用<code>class Meta </code>作为作者模型<code>Author</code>(可能不像定制基于类的视图那样灵活,但很容易),您最终会得到这样的结果:</p>
+
+<pre>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)
+
+<strong> class Meta:
+ ordering = ['last_name']</strong></pre>
+
+<p>当然,该字段不需要是<code>last_name</code>:它可以是任何其他字段。</p>
+
+<p>最后,但并非最不重要的是,您应该按照实际上在数据库上具有索引(唯一或非唯一)的属性/栏位进行排序,以避免性能问题。当然,如果这么少量的书本(和用户!),这里就没有必要(我们可能会让自己提前做太多事情),但是对于未来的项目来说,这是需要考虑的事情。</p>
+</div>
+
+<h2 id="它看起来是什么样子?_2">它看起来是什么样子?</h2>
+
+<p>此时,我们应该创建了显示书本列表,和书本详细信息页面所需的所有内容。运行服务器(<code>python3 manage.py runserver</code>),并打开浏览器到 <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>。</p>
+
+<div class="warning">
+<p><strong>警告:</strong> <span id="result_box" lang="zh-CN"><span>请还不要点击任何作者、或作者详细信息链接 - 您将在挑战练习中,创建这些链接!</span></span></p>
+</div>
+
+<p>单击所有书籍链接<strong> All books</strong> ,以显示书籍列表。</p>
+
+<p><img alt="Book List Page" src="https://mdn.mozillademos.org/files/14049/book_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 823px;"></p>
+
+<p>然后点击指向您的某本图书的链接。如果一切设置正确,您应该看到类似下面的屏幕截图。</p>
+
+<p><img alt="Book Detail Page" src="https://mdn.mozillademos.org/files/14051/book_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 783px; margin: 0px auto; width: 926px;"></p>
+
+<h2 id="分页">分页</h2>
+
+<p>如果您刚刚获得了一些记录,我们的图书清单页面看起来会很好。但是,当您进入数十或数百条记录的页面时,页面将逐渐花费更长时间加载(并且有太多内容无法合理浏览)。此问题的解决方案,是为列表视图添加分页,减少每页上显示的项目数。</p>
+
+<p>Django 在分页方面,拥有出色的内置支持。更好的是,它内置于基于类的通用列表视图中,因此您无需执行太多操作即可启用它!</p>
+
+<h3 id="视图">视图</h3>
+
+<p>打开 <strong>catalog/views.py</strong>,然后添加下面粗体显示的<code>paginate_by </code>行。</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+ <strong>paginate_by = 10</strong></pre>
+
+<p>通过添加这行,只要您有超过10条记录,视图就会开始对它发送到模板的数据,进行分页。使用 GET 参数访问不同的页面 - 要访问第2页,您将使用URL:<code>/catalog/books/<strong>?page=2</strong></code>。</p>
+
+<h3 id="模板">模板</h3>
+
+<p>现在数据已经分页,我们需要添加对模板的支持,以滚动结果集合。因为我们可能希望在所有列表视图中,都执行此操作,所以我们将以可添加到基本模板的方式,执行此操作。</p>
+
+<p>打开 <strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong>,并复制贴士以下内容区块下面的分页区块(以粗体突出显示)。代码首先检查当前页面上,是否启用了分页。如果是,则它会根据需要,添加下一个和上一个链接(以及当前页码)。</p>
+
+<pre class="brush: python">{% block content %}{% endblock %}
+
+<strong>{% block pagination %}
+ {% if is_paginated %}
+ &lt;div class="pagination"&gt;
+ &lt;span class="page-links"&gt;
+ {% if page_obj.has_previous %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}"&gt;previous&lt;/a&gt;
+ {% endif %}
+ &lt;span class="page-current"&gt;
+ Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
+ &lt;/span&gt;
+ {% if page_obj.has_next %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}"&gt;next&lt;/a&gt;
+ {% endif %}
+ &lt;/span&gt;
+ &lt;/div&gt;
+ {% endif %}
+{% endblock %} </strong></pre>
+
+<p><code>page_obj </code>是一个 <a href="https://docs.djangoproject.com/en/2.0/topics/pagination/#paginator-objects">Paginator</a> 对象,如果在当前页面上使用分页,它将存在。 它允许您获取有关当前页面,之前页面,有多少页面等的所有信息。</p>
+
+<p>我们使用 <code>\{{ request.path }}</code>,来获取用于创建分页链接的当前页面URL。 这很有用,因为它独立于我们正在分页的对象。</p>
+
+<p>就是这样!</p>
+
+<h3 id="它看起来是什么样子的?">它看起来是什么样子的?</h3>
+
+<p>下面的屏幕截图,显示了分页的样子 - 如果您没有在数据库中输入超过10个标题,那么您可以通过降低 <strong>catalog/views.py </strong>文件中 <code>paginate_by </code>行指定的数量,来更轻松地测试它。 为了得到以下结果,我们将其更改为 <code>paginate_by = 2</code>。</p>
+
+<p>分页链接显示在底部,根据您所在的页面,显示下一个/上一个链接。</p>
+
+<p><img alt="Book List Page - paginated" src="https://mdn.mozillademos.org/files/14057/book_list_paginated.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 924px;"></p>
+
+<h2 id="挑战自己">挑战自己</h2>
+
+<p>本文中的挑战,是创建完成项目所需的作者详细信息视图,和列表视图。这些应在以下URL中提供:</p>
+
+<ul>
+ <li><code>catalog/authors/</code> — 所有作者的名单。</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— 特定作者的详细视图,并具有名为<em><code>&lt;id&gt;</code></em>的主键字段</li>
+</ul>
+
+<p>URL 映射器和视图所需的代码,应与我们上面创建的<code>Book</code>列表和详细视图几乎完全相同。模板将有所不同,但会分享类似的行为。</p>
+
+<p> </p>
+
+<div class="note">
+<p><strong>注意</strong>:</p>
+
+<ul>
+ <li>为作者列表页面,创建URL映射器之后,还需要更新基本模板中的所有作者 <strong>All authors </strong>链接。按照我们更新“所有图书”<strong>All books</strong> 链接时,所做的相同过程。</li>
+ <li>为作者详细信息页面,创建URL映射器之后,还应更新书本详细信息视图模板(<strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>),以便作者链接,指向新的作者详细信息页面(而不是一个空的URL)。该行将更改为添加下面以粗体显示的模板标记。
+ <pre class="brush: html">&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt;
+</pre>
+ </li>
+</ul>
+</div>
+
+<p>完成后,您的页面应该类似于下面的屏幕截图。</p>
+
+<p><img alt="Author List Page" src="https://mdn.mozillademos.org/files/14053/author_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+</ul>
+
+<p><img alt="Author Detail Page" src="https://mdn.mozillademos.org/files/14055/author_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 358px; margin: 0px auto; width: 825px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>恭喜,我们的图书馆的基本功能现在完成了!</p>
+
+<p>本文中,我们学到如何使用基于类别的通用列表视图与详细视图,并使用它们创建页面,以查看我们的书本和作者。在此过程中,我们了解了与正则表达式匹配的模式,以及如何将数据从URL传递到视图。我们还学习了一些使用模板的技巧。最后,我们已经展示了如何对列表视图进行分页,这样即使我们有很多记录,我们也可以管理列表。</p>
+
+<p>在我们的下一篇文章,我们将扩充此图书馆,以支持使用者帐户,并从而演示使用者授权、许可、授权、会话, 以及表单。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/">Built-in class-based generic views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/">Generic display views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/class-based-views/intro/">Introduction to class-based views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/templates/builtins">Built-in template tags and filters</a> (Django docs).</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/topics/pagination/">Pagination</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="本教程">本教程</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Introduction">Django 介绍</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/development_environment">架设 Django 开发环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Django 教程: 本地图书馆网站</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django 教程 2: 创建骨架站点</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Models">Django 教程 3: 使用模型</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Admin_site">Django 教程 4: Django 管理站点</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django 教程 5: 创建主页</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django 教程 6: 通用列表与详细信息视图</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django 教程 7: 会话框架</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Authentication">Django 教程 8: 用户认证与许可</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Forms">Django 教程 9: 使用表单</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Testing">Django 教程 10: 测试 Django 网页应用</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/Deployment">Django 教程 11: 部署 Django 到生产环境</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/web_application_security">Django 网页应用安全</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django 微博客</a></li>
+</ul>
+
+<p> </p>