Django Tutorial Part 6: Generic list and detail views

 

本教程扩展了我们的LocalLibrary网站,添加了书籍和作者的列表和详细信息页面. 在这里,我们将学习基于通用类的视图,并展示它们如何减少常见用例所必须编写的代码量. 我们还将更详细地介绍URL处理,展示如何执行基本的模式匹配.

Prerequisites: 完成所有先前的教程主题,包括Django教程第5部分:创建我们的主页 .
Objective: 了解在哪里以及如何使用基于类的通用视图,以及如何从URL中提取模式并将信息传递给视图.

Overview

在本教程中,我们将通过添加书籍和作者的列表和详细信息页面来完成LocalLibrary网站的第一个版本(或更准确地说,我们将向您展示如何实现书籍页面,并让您创建作者分页!)

此过程类似于创建索引页的过程,我们在上一教程中已显示了该过程. 我们仍然需要创建URL映射,视图和模板. 主要区别在于,对于详细信息页面,我们将面临另一个挑战,即从URL的模式中提取信息并将其传递给视图. 对于这些页面,我们将演示一种完全不同的视图类型:基于类的通用列表视图和详细视图. 这些可以大大减少所需的视图代码量,从而使其更易于编写和维护.

本教程的最后一部分将演示如何使用基于类的通用列表视图对数据进行分页.

Book list page

图书清单页面将显示该页面中所有可用图书记录的列表,可使用URL: catalog/books/ . 该页面将显示每个记录的标题和作者,标题是指向相关书籍详细信息页面的超链接. 该页面将具有与站点中所有其他页面相同的结构和导航,因此,我们可以扩展在上一个教程中创建的基本模板( base_generic.html ).

URL mapping

打开/catalog/urls.py并复制下面以粗体显示的行. 对于索引页,此path()函数定义一个与URL( 'books /' )相匹配的模式,一个与URL匹配的view函数( views.BookListView.as_view() )和一个名称.对于此特定映射.

urlpatterns = [
    path('', views.index, name='index'),
    path('books/', views.BookListView.as_view(), name='books'),
]

如上一教程中所述,URL必须已经匹配了/catalog ,因此实际上将为URL调用视图: /catalog/books/ .

视图函数具有与以前不同的格式-这是因为此视图实际上将作为类实现. 我们将从现有的通用视图函数中继承,该函数已经完成了我们希望该视图函数执行的大部分操作,而不是从头开始编写自己的视图函数.

对于基于Django类的视图,我们通过调用类方法as_view()访问适当的视图函数. 这完成了创建类实例的所有工作,并确保为传入的HTTP请求调用正确的处理程序方法.

View (class-based)

我们可以很容易地将书籍列表视图编写为常规函数(就像我们以前的索引视图一样),该函数将在数据库中查询所有书籍,然后调用render()将列表传递给指定的模板. 相反,我们将使用基于类的通用列表视图( ListView )-从现有视图继承的类. 因为通用视图已经实现了我们所需的大多数功能,并且遵循了Django的最佳实践,所以我们将能够以更少的代码,更少的重复并最终减少维护来创建更强大的列表视图.

打开catalog / views.py ,并将以下代码复制到文件底部:

from django.views import generic

class BookListView(generic.ListView):
    model = Book

而已! 通用视图将查询数据库以获取指定模型( Book )的所有记录,然后呈现位于/locallibrary/catalog/templates/catalog/book_list.html的模板(我们将在下面创建). 在模板内,您可以使用名为object_listbook_list的模板变量(即,通常为" the_model_name _list ")访问书籍列表.

:为模板位置这尴尬的路径是不是印错-通用视图寻找模板/ application_name / the_model_name _list.htmlcatalog/book_list.html在这种情况下)应用程序的内部/ application_name /templates/目录( /catalog/templates/) .

您可以添加属性以更改上述默认行为. 例如,如果需要具有使用同一模型的多个视图,则可以指定另一个模板文件,或者如果book_list对于您的特定模板用例不直观,则可能要使用其他模板变量名. 可能最有用的变化是更改/过滤返回结果的子集-因此,除了列出所有书籍之外,您还可以列出其他用户阅读过的前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

Overriding methods in class-based views

尽管我们在这里不需要这样做,但是您也可以重写某些类方法.

例如,我们可以重写get_queryset()方法来更改返回的记录列表. 这比像前面的代码片段中那样设置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

执行此操作时,务必遵循上面使用的模式:

  • 首先从我们的超类获取现有上下文.
  • 然后添加新的上下文信息.
  • 然后返回新的(更新的)上下文.

注意 :请查看内置的基于类的通用视图 (Django docs),以获取更多有关您可以执行的操作的示例.

Creating the List View template

创建HTML文件/locallibrary/catalog/templates/catalog/book_list.html并复制以下文本. 如上所述,这是通用的基于类的列表视图(对于名为catalog的应用程序中名为Book的模型)所期望的默认模板文件.

通用视图的模板与其他模板一样(尽管传递给模板的上下文/信息当然可能会有所不同). 与索引模板一样,我们在第一行中扩展基本模板,然后替换名为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 %}

该视图默认将上下文(书籍列表)作为object_listbook_list别名book_list ; 两者都会起作用.

Conditional execution

我们使用ifelseendif模板标记来检查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 %} )在其他条件下进行测试. 有关条件运算符的更多信息,请参见: ififequal / ifnotequalifif内置模板标记和过滤器 (Django Docs)中进行了更改 .

For loops

模板使用forendfor模板标记在书单中循环,如下所示. 每次迭代都使用当前列表项的信息填充book模板变量.

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

尽管在此未使用,但在循环中,Django还将创建其他变量,您可以使用这些变量来跟踪迭代. 例如,您可以测试forloop.last变量以在最后一次运行循环时执行条件处理.

Accessing variables

循环内的代码为每本书创建一个列表项,该列表项既显示标题(作为尚未创建的详细视图的链接)又显示作者.

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

我们使用"点符号"(例如book.titlebook.author )访问关联的书记录的字段 ,其中book项后的文本是字段名称(在模型中定义).

我们还可以从模板中调用模型中的函数 -在这种情况下,我们调用Book.get_absolute_url()以获得可用于显示关联明细记录的URL. 如果该函数没有任何参数(没有传递参数的方法!),此方法将起作用.

注意 :在模板中调用函数时,我们必须小心"副作用". 在这里,我们仅显示一个URL,但是一个函数可以执行几乎所有操作-我们不想仅通过呈现模板来删除数据库(例如)!

Update the base template

打开基本模板( / locallibrary / catalog / templates / base_generic.html ),然后将{% url'books '%}插入" All books"的URL链接,如下所示. 这将启用所有页面中的链接(既然我们已经创建了" books" 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>

What does it look like?

您将无法建立图书清单,因为我们仍然缺少依赖项-图书详细信息页面的URL映射,这是创建指向各个图书的超链接所必需的. 在下一部分之后,我们将同时显示列表视图和详细信息视图.

Book detail page

书籍详细信息页面将显示有关特定书籍的信息,可以使用URL catalog/book/ <id> (其中<id>是书籍的主键)进行访问. 除了Book模型中的字段(作者,摘要,ISBN,语言和体裁)外,我们还将列出可用副本的详细信息( BookInstances ),包括状态,预计退货日期,版本说明和ID. 这将使我们的读者不仅可以了解这本书,还可以确认是否有书.

URL mapping

打开/catalog/urls.py并添加下面以粗体显示的' book- detail'URL映射器. 此path()函数定义模式,关联的基于通用类的详细信息视图和名称.

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的一部分,其中包含了视图可用于访问捕获的数据的变量的名称. 例如, <something> ,将捕获标记的模式并将值作为变量" something"传递给视图. 您可以选择在变量名称之前添加一个转换器规范 ,该转换器规范定义了数据类型(int,str,slug,uuid,path).

在这种情况下,我们使用'<int:pk>' 捕获图书ID,该ID必须为特殊格式的字符串,并将其作为名为pk (主键的缩写)的参数传递给视图. 这是用来在书籍模型中定义的唯一将书籍存储在数据库中的ID.

注意 :如前所述,我们匹配的URL实际上是catalog/book/<digits> (因为我们在目录应用程序中,所以假定为/catalog/ ).

重要提示 :基于类的通用详细视图希望传递一个名为pk的参数. 如果要编写自己的函数视图,则可以使用任何您喜欢的参数名称,或者实际上将信息传递给未命名的参数.

Advanced path matching/regular expression primer

注意 :您将不需要本节来完成本教程! 我们之所以提供此功能,是因为了解此选项可能对以Django为中心的未来很有用.

path()提供的模式匹配对于您只想捕获任何字符串或整数的(非常常见的)情况而言,简单而有用. 如果您需要更精细的过滤(例如,仅过滤具有一定数量字符的字符串),则可以使用re_path()方法.

该方法的使用方式与path()相同,不同之处在于,它允许您使用正则表达式指定模式. 例如,以前的路径可能如下所示:

re_path(r'^book/(?P<pk>\d+)$', views.BookDetailView.as_view(), name='book-detail'),

正则表达式是一种非常强大的模式映射工具. 坦率地说,它们非常不直观,可能会对初学者造成威胁. 下面是一个很短的入门书!

首先要知道的是,通常应使用原始字符串文字语法声明正则表达式(即,将其括起来如下所示: r'<您的正则表达式文本在此处>' ).

声明模式匹配时需要了解的语法的主要部分是:

Symbol Meaning
^ 匹配文字的开头
$ 匹配文字结尾
\d 匹配一个数字(0,1,2,... 9)
\w 匹配单词字符,例如,字母,数字或下划线字符(_)中的任何大写或小写字符
+ 匹配一个或多个前面的字符. 例如,要匹配一个或多个数字,可以使用\d+ . 要匹配一个或多个" a"字符,可以使用a+
* 匹配零个或多个前一个字符. 例如,不匹配任何单词或单词,可以使用\w*
( ) 将花样的一部分留在括号内. 任何捕获的值都将作为未命名参数传递给视图(如果捕获了多个模式,则将按照声明捕获的顺序提供关联的参数).
(?P<name>...) 将模式(由...表示)捕获为命名变量(在本例中为"名称"). 捕获的值将使用指定的名称传递到视图. 因此,您的视图必须声明一个具有相同名称的参数!
[] 匹配集合中的一个字符. 例如,[abc]将匹配'a'或'b'或'c'. [-\ w]将匹配'-'字符或任何单词字符.

其他大多数字符都可以按字面意义使用!

让我们考虑一些模式的真实示例:

Pattern Description
r'^book/(?P<pk>\d+)$'

这是我们的URL映射器中使用的RE. 它具有一个字符串匹配book/在该行的开始(^书/),则有一个或多个数字( \d+ ),然后结束(用线标记的结束之前没有非数字字符).

它还捕获所有数字(?P <pk> \ d +) ,并将它们传递到名为" pk"的参数中的视图. 捕获的值始终以字符串形式传递!

例如,这将匹配book/1234 ,并将变量pk='1234'发送到视图.

r'^book/(\d+)$' 这与前面的情况匹配相同的URL. 捕获的信息将作为未命名的参数发送到视图.
r'^book/(?P<stub>[-\w]+)$'

这具有字符串匹配book/在该行的开始(^书/),则具有一个或多个字符或者是一个" - "或字字符([ - \ W] +),然后结束. 它还捕获这组字符,并将它们通过名为" stub"的参数传递给视图.

这是"存根"的相当典型的模式. 存根是URL友好的基于单词的数据主键. 如果您希望您的书本网址更具信息性,则可以使用存根. 例如/catalog/book/the-secret-garden而不是/catalog/book/33 .

您可以在一次匹配中捕获多种模式,从而在URL中编码许多不同的信息.

注意 :作为一个挑战,请考虑如何编码URL,以列出特定年份,月份,日期以及可用于匹配该URL的RE的所有书籍.

Passing additional options in your URL maps

我们没有在这里使用过但您可能会发现有价值的功能之一是,您可以声明其他选项并将其传递给视图. 这些选项被声明为字典,您将其作为第三个未命名的参数传递给path()函数. 如果您想对多个资源使用相同的视图,并在每种情况下传递数据以配置其行为(以下我们分别提供不同的模板),则此方法很有用.

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'),

Note: Both extra options and named captured patterns are passed to the view as named arguments. If you use the 一样的名字 for both a captured pattern and an extra option then only the captured pattern value will be sent to the view (the value specified in the additional option will be dropped). 

View (class-based)

打开catalog / views.py ,并将以下代码复制到文件底部:

class BookDetailView(generic.DetailView):
    model = Book

而已! 现在,您需要做的就是创建一个名为/locallibrary/catalog/templates/catalog/book_detail.html的模板,该视图将向其传递URL映射器提取的特定Book记录的数据库信息. 在模板内,您可以使用名为objectbook的模板变量(即,通常为" the_model_name ")访问书籍列表.

如果需要,可以更改使用的模板以及用于在模板中引用书籍的上下文对象的名称. 您还可以重写方法,例如,将其他信息添加到上下文中.

What happens if the record doesn't exist?

如果请求的记录不存在,则基于类的通用详细视图将自动为您引发Http404异常-在生产中,这将自动显示相应的"找不到资源"页面,您可以根据需要自定义该页面.

只是为了让您了解它是如何工作的,下面的代码片段演示了如果使用基于类的通用详细视图,则如何将基于类的视图实现为一个函数.

def book_detail_view(request, primary_key):
    try:
        book = Book.objects.get(pk=primary_key)
    except Book.DoesNotExist:
        raise Http404('Book does not exist')
    
    return render(request, 'catalog/book_detail.html', context={'book': book})

视图首先尝试从模型中获取特定的书记录. 如果失败,则视图应引发Http404异常,以指示该书"未找到". 然后,通常,最后一步是使用context参数(作为字典)中的模板名称和书籍数据调用render() ).

另外,如果找不到记录,我们可以使用get_object_or_404()函数作为引发Http404异常的快捷方式.

from django.shortcuts import get_object_or_404

def book_detail_view(request, primary_key):
    book = get_object_or_404(Book, pk=primary_key)
    return render(request, 'catalog/book_detail.html', context={'book': book})

Creating the Detail View template

创建HTML文件/locallibrary/catalog/templates/catalog/book_detail.html并提供以下内容. 如上所述,这是通用的基于类的详细信息视图(对于名为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> {{ book.genre.all|join:", " }}</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 %}

上面模板中的author链接具有一个空URL,因为我们尚未创建一个author detail页面. 一旦存在,您应该像这样更新URL:

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

Though a little larger, almost everything in this template has been described previously:

  • 我们扩展基本模板并覆盖"内容"块.
  • 我们使用条件处理来确定是否显示特定内容.
  • 我们使用for循环遍历对象列表.
  • 我们使用点表示法访问上下文字段(因为我们使用了细节通用视图,因此该上下文被命名为book ;我们也可以使用" object ")

我们以前从未见过的一件有趣的事情是功能book.bookinstance_set.all() . 此方法由Django"自动"构造,以便返回与特定Book关联的BookInstance记录集.

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

之所以需要此方法,是因为您仅在关系的"一侧"声明了ForeignKey (一对多)字段. 由于您不做任何事来声明其他("许多")模型中的关系,因此它没有任何字段可获取关联记录集. 为了克服这个问题,Django构造了一个可以适当使用的名为"反向查找"的函数. 该函数的名称通过以下方式构造:在声明ForeignKey的模型名称后面加上bookinstance_set() ,然后加上_set (即,在Book创建的函数为bookinstance_set() ).

注意 :这里我们使用all()获取所有记录(默认). 尽管可以使用filter()方法获取代码中的记录子集,但是由于无法指定函数的参数,因此不能直接在模板中执行此操作.

还请注意,如果您未定义订单(在基于类的视图或模型上),您还将看到来自开发服务器的错误,如下所示:

[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)

发生这种情况是因为分页程序对象希望看到您的基础数据库上正在执行某些ORDER BY. 没有它,就不能确定返回的记录实际上是正确的顺序!

本教程尚未介绍分页 (尚未!),但是由于您不能使用sort_by()并传递参数(与上述filter()相同),因此您必须在以下三个选项之间进行选择:

  1. 在模型的class Meta声明内添加ordering .
  2. 在您的基于类的自定义视图中添加一个queryset属性,并指定一个order_by() .
  3. get_queryset方法添加到基于类的自定义视图中,并指定order_by() .

如果您决定为Author模型使用一个class Meta (可能不像自定义基于类的视图那样灵活,但是很简单),您将得到如下结果:

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 f'{self.last_name}, {self.first_name}'

    class Meta:
        ordering = ['last_name']

当然,该字段不必为last_name :它可以是其他任何字段.

最后但并非最不重要的一点是,您应按在数据库上实际具有索引(唯一或不唯一)的属性/列进行排序,以避免性能问题. 当然,这里并不需要这样做(我们可能会以很少的书籍和用户来超越自己),但是在将来的项目中要牢记这一点.

What does it look like?

在这一点上,我们应该已经创建了同时显示图书清单和图书详细信息页面所需的所有内容. 运行服务器( python3 manage.py runserver )并打开浏览器到http://127.0.0.1:8000/ .

警告:请不要单击任何作者或作者详细信息链接-您将在挑战中创建这些链接!

单击所有书籍链接以显示书籍列表.

Book List Page

然后单击指向您的一本书的链接. 如果一切设置正确,您应该会看到类似以下屏幕截图的内容.

Book Detail Page

Pagination

如果您只有几条记录,那么我们的图书清单页面会很好. 但是,当您进入数十或数百条记录时,页面的加载时间将逐渐变长(并且内容太多,无法明智地浏览). 解决此问题的方法是将分页添加到列表视图中,以减少每个页面上显示的项目数.

Django具有出色的内置分页支持. 更好的是,它内置在基于类的通用列表视图中,因此您无需做很多事情就可以启用它!

Views

打开catalog / views.py ,并添加下面以粗体显示的paginate_by行.

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

有了这个添加,只要您有10条以上的记录,视图就会开始对发送给模板的数据进行分页. 使用GET参数访问不同的页面-要访问第2页,您将使用URL /catalog/books/ ?page=2 .

Templates

现在已经对数据进行了分页,我们需要向模板添加支持以滚动浏览结果集. 因为我们可能想在所有列表视图中执行此操作,所以我们将以可以添加到基本模板中的方式执行此操作.

打开/ locallibrary / catalog / templates / base_generic.html并复制到我们内容块下方的以下分页块中(以下以粗体突出显示). 该代码首先检查当前页面上是否启用了分页. 如果是这样,它将在适当时添加下一个和上一个链接(以及当前页码).

{% 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对象,如果在当前页面上使用分页,该对象将存在. 它使您可以获得有关当前页面,上一页,有多少页等的所有信息.

我们使用{{ request.path }}来获取用于创建分页链接的当前页面URL. 这很有用,因为它与我们正在分页的对象无关.

而已!

What does it look like?

The screenshot below shows what the pagination looks like — if you haven't entered more than 10 titles into your database, then you can test it more easily by lowering the number specified in the paginate_by line in your catalog/views.py file. To get the below result we changed it to paginate_by = 2.

分页链接显示在底部,根据您所在的页面显示下一个/上一个链接.

Book List Page - paginated

Challenge yourself

本文中的挑战是创建作者详细信息并列出完成项目所需的视图. 这些应通过以下URL提供:

  • catalog/authors/ —所有作者的列表.
  • catalog/author/ <id>   —具有名为<id>的主键字段的特定作者的详细信息视图

URL映射器和视图所需的代码应与我们上面创建的Book清单"和"详细视图Book实质上相同. 模板将有所不同,但是将共享相似的行为.

Note:

  • 为作者列表页面创建URL映射器后,您还需要更新基本模板中的" 所有作者"链接. 遵循更新" 所有图书"链接时相同的过程 .
  • 为作者详细信息页面创建URL映射器后,还应该更新图书详细信息视图模板/locallibrary/catalog/templates/catalog/book_detail.html ),以便作者链接指向您的新作者详细信息页面(而不是一个空URL). 该行将更改为添加下面以粗体显示的模板标记.
      <p> <strong>作者:</ strong> <a href=" {% url'author-detail' book.author.pk %} "> {{book.author}} </a> </ p> 
    

完成后,您的页面应类似于以下屏幕截图.

Author List Page

Author Detail Page

Summary

恭喜,我们的基本库功能现已完成!

在本文中,我们学习了如何使用基于类的通用列表和详细视图,并使用它们来创建页面以查看我们的书和作者. 在此过程中,我们学习了使用正则表达式进行模式匹配的方法,以及如何将数据从URL传递到视图. 我们还学习了一些使用模板的技巧. 最后,我们展示了如何对列表视图进行分页,以便即使有很多记录也可以对列表进行管理.

在我们的下一篇文章中,我们将扩展该库以支持用户帐户,从而演示用户身份验证,权限,会话和表单.

See also

In this module