We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

Learn web development

Django Tutorial Part 6: Generic list and detail views

此页面上有脚本错误。虽然它是写给网站编辑,您可以在下面查看部分内容。

这篇翻译不完整。请帮忙从英语翻译这篇文章

本教程的扩展我们的LocalLibrary网站,添加列表和书和作者详细页面。在这里,我们将学习泛型类视图,并展示它们如何减少为常见用例编写的代码量。我们还将更详细地讨论URL处理,演示如何执行基本的模式匹配。

Prerequisites: Complete all previous tutorial topics, including Django Tutorial Part 5: Creating our home page.
Objective: To understand where and how to use generic class-based views, and how to extract patterns from URLs and pass the information to views.

概观

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

这个过程类似于我们在前面的教程中展示的:创建索引页面。我们仍然需要创建URL地图,视图和模板。主要区别在于,对于详细信息页面,我们还需要从URL中的模式中提取信息并将其传递到视图。对于这些页面,我们将展示一个完全不同类型的视图:通用的基于类的列表视图和详细视图。这些可以显着减少所需的视图代码的数量,使其更易于编写和维护。

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

图书清单页面

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

URL映射

打开/catalog/urls.py并复制下面粗体显示的行。至于索引页面,这个path()函数定义了一个模式来匹配URL('books /'),一个视图函数,如果URL匹配(views.BookListView.as_view()),将被调用,并且这个特定的映射的名字。

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

正如在前面的教程中讨论的URL必须已经匹配/catalog,所以认为实际上将呼吁网址:/catalog/books/

视图函数的格式与以前不同 - 这是因为这个视图实际上是作为一个类来实现的。我们将从一个现有的通用视图函数继承,而这个视图函数已经完成了我们希望这个视图函数所做的大部分工作,而不是从头开始编写我们自己的。

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

View (class-based)

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

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

    from django.views import generic

    class BookListView(generic.ListView):
        model = Book

通用视图将查询数据库以获取指定模型的所有记录 (Book) 然后渲染位于 /locallibrary/catalog/templates/catalog/book_list.html (我们将在下面创建) 的模板。 在模板中,您可以使用名为 object_list OR book_list (i.e. generically "the_model_name_list") 的模板变量访问书籍列表。

注意: 模板位置的这种尴尬路径并非印刷错误— the generic views look for templates in /application_name/the_model_name_list.html (catalog/book_list.html in this case) inside the application's /application_name/templates/ directory (/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

在基于类的视图中重写方法

虽然我们在这里不需要这样做,但您也可以重写某些类方法。

例如,我们可以重写该 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

当这样做时,遵循以上使用的模式很重要:

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

Note: 查看 Built-in class-based generic views (Django docs) 以获取更多您可以执行的操作的更多示例。

创建列表视图模板

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

通用视图的模板就像任何其他模板一样(尽管传递给模板的上下文/信息当然可能不同)。与我们的索引模板一样,我们在第一行中扩展基本模板,然后替换名为的块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 别名; 要么会工作。

有条件的执行

我们使用 if, else and 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 模板标记 (e.g. {% elif var2 %} ) 在附加条件下进行测试。For more information about conditional operators see: if, ifequal/ifnotequal, and ifchanged in Built-in template tags and filters (Django Docs).

For 循环

该模板使用 for and endfor template 标签遍历书籍列表,如下所示。每次迭代 book 使用当前列表项的信息填充模板变量。

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

虽然没有在这里使用,但在循环内Django也会创建其他变量,您可以使用它来跟踪迭代。例如,您可以测试该forloop.last变量以在上一次运行循环时执行条件处理。

访问变量

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

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

We access the fields of the associated book record using the "dot notation" (e.g. book.title and book.author), where the text following the book item is the field name (as defined in the model).

我们也可以在模板中调用模型中的函数 - 在这种情况下,我们调用Book.get_absolute_url()获取可用于显示关联的详细信息记录的URL。这个工作提供的功能没有任何参数(没有办法传递参数!)

注意:在模板中调用函数时,我们必须小心一些“副作用”。在这里我们只需要显示一个URL,但是一个函数可以做任何事情 - 我们不想通过渲染我们的模板来删除我们的数据库(例如)!

更新基本模板

打开基本模板(/ locallibrary / catalog / templates / base_generic.html)并将{%url'books '%} 插入到所有图书的URL链接中,如下所示。这将启用所有页面中的链接(我们现在可以成功地将其放置到位,因为我们已经创建了“书籍”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>

它是什么样子的?

您将无法构建书籍列表,因为我们仍然缺少依赖项 - 图书详细信息页的URL地图,这需要创建单个图书的超链接。我们将在下一节后显示列表视图和详细视图。

书详情页面

书籍详细信息页面将显示有关特定图书的信息,可通过URL访问本书的主要关键位置)。模型中的字段(作者,摘要,ISBN,语言和流派)外,我们还会列出可用副本(的详细信息,包括状态,预期返回日期,版本记录和ID。这将使我们的读者不仅可以了解该书,还可以确认它是否/何时可用。catalog/book/<id><id>BookBookInstances

URL 映射

打开/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(必须是整数),并将其作为参数pk (主键的缩写)传递给视图

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

重要提示:通用的基于类的详细视图预计会传入一个名为pk的参数如果你正在编写你自己的函数视图,你可以使用你喜欢的任何参数名称,或者确实在一个未命名的参数中传递信息。

高级路径匹配/正则表达式引物

注意: 您不需要此部分来完成教程!我们提供它是因为知道这个选项可能对您的Django为中心的未来有用。

 

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

此方法的使用  path()方式与使用正则表达式指定模式不同  例如,前面的路径可以写成如下所示:

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

正则表达式是一个非常强大的模式映射工具。坦率地说,它们对于初学者来说非常不直观和可怕。下面是一个非常简短的入门书!

你需要知道的声明模式匹配的主要部分是:

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/在行首(^ book /的字符串,然后有一个或多个数字(\d+),然后结束(在行标记结束之前没有非数字字符)。

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

例如,这会匹配并向视图book/1234发送一个变量pk='1234'

r'^book/(\d+)$' 这匹配与上述案例相同的网址。捕获的信息将作为未命名的参数发送到视图。
r'^book/(?P<stub>[-\w]+)$'

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

这是“存根”的典型模式。存根是用于数据的URL友好的基于字的主键。如果您希望自己的书籍URL更具信息性,您可以使用存根。例如,/catalog/book/the-secret-garden而不是/catalog/book/33

您可以捕获一个匹配中的多个模式,因此可以在URL中编码大量不同的信息。

注意: 作为一项挑战,请考虑如何编码url以列出特定年份,月份,日期中发布的所有书籍以及可用于匹配它的RE。


在URL地图中传递更多选项

我们没有在这里使用过的一个功能,但您可能会发现有价值的是,您可以声明并向视图传递其他选项。这些选项被声明为一个字典,作为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'),
    

注:额外的选项和命名的捕获模式都作为

View (class-based)

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

    class BookDetailView(generic.DetailView):
        model = Book

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

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

如果记录不存在会发生什么?

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

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

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

该视图首先尝试从模型中获取特定的书籍记录。如果失败,视图应该引发Http404异常,表明该书“未找到”。像往常一样,最后一步是render()使用参数中的模板名称和书籍数据context(作为字典)进行调用。

注意:(get_object_or_404()如上所述注释)是一条方便的捷径,用于Http404在找不到记录时引发异常。

创建详细视图模板

创建HTML文件/locallibrary/catalog/templates/catalog/book_detail.html为其提供以下内容。如上所述,这是通用基于类的

    {% 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,因为我们尚未创建作者详细信息页面。一旦存在,你应该像这样更新URL:

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

虽然有点大,但这个模板中的几乎所有内容都已经在之前描述过了:

  • 我们扩展我们的基础模板并覆盖“内容”块。
  • 我们使用条件处理来确定是否显示特定内容。
  • 我们使用for循环遍历对象列表。
  • 我们使用点符号访问上下文字段(因为我们已经使用了详细的通用视图,上下文被命名book;我们也可以使用“ object”)

我们以前没有见过的一件有趣的事是功能book.bookinstance_set.all()。这个方法是由Django“自动”构造的,以便返回BookInstance与特定关联的记录集合Book

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

此方法是必需的,因为您ForeignKey 只在关系的“一个”一侧声明(一对多)字段。既然你没有做任何事情来声明其他(“多”)模型中的关系,它没有任何字段来获得关联记录集合。为了解决这个问题,Django构建了一个可以使用的适当命名的“反向查找”函数。该函数的名称由ForeignKey 声明后的模型名称构成,后面跟着_set(即,所创建的函数Bookbookinstance_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)
    

发生这种情况是因为paginator对象期望在您的下划线数据库中看到一些ORDER BY正在执行。如果没有它,不能确定寄回的登记册实际上是否正确!

本教程没有达到分页(但是很快,但很快),但由于您不能使用sort_by()和传递参数(与filter()上述相同),您将不得不在三种选择之间进行选择:

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

如果你决定用走class MetaAuthor模式(可能不够灵活定制的基于类的观点,但很容易),你将最终是这样的:

    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:它可以是其他的。

最后但并非最不重要的一点是,您应该对数据库中实际具有索引(唯一或不是)的属性/列进行排序,以避免性能问题。当然,如果这样少量的图书(和用户!)在这里没有必要(而且我们可能让自己超前了),但是对于未来的项目而言,这是必须牢记的。

它看起来是什么样子的?

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

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

点击所有图书链接显示图书列表。 

Book List Page

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

Book Detail Page

分页

如果您刚收到一些记录,我们的图书清单页面就会显得很好。但是,当您进入数十或数百条记录时,页面的加载时间将会逐渐变长(并且有太多内容需要浏览)。解决此问题的方法是将分页添加到列表视图中,从而减少每个页面上显示的项目数量。 

Django具有出色的内置分页支持。更好的是,这是建立在通用的基于类的列表视图中,因此您不必为此启用它!

Views

打开目录/ views.py,并添加paginate_by下面以粗体显示行。

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

有了这个补充,只要有超过10条记录,视图就会开始分页发送给模板的数据。不同的页面使用GET访问参数-访问2页,你会使用的网址:  /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来创建分页链接。这很有用,因为它与我们分页的对象无关。

Thats it!

它是什么样子的?

下面的屏幕截图显示了分页的样子 - 如果您未在数据库中输入超过10个标题,则可以通过降低catalog / views.py文件中的paginate_by行中指定的数量来更轻松地进行测试为了得到下面的结果,我们将其改为paginate_by = 2

分页链接显示在底部,显示下一个/上一个链接,具体取决于您所在的页面。

Book List Page - paginated

挑战自我

本文中的挑战是创建完成项目所需的作者详细信息和列表视图。这些应在以下网址提供:

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

URL映射器和视图所需的代码实际上应与Book我们上面创建列表和详细视图相同模板会有所不同,但会共享类似的行为。

注意

  • 为作者列表页面创建URL映射器后,您还需要更新基础模板中所有作者链接。按照我们更新“ 所有书籍”链接相同流程
  • 为作者详细信息页面创建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> 
        

 

完成后,您的页面应该看起来像下面的屏幕截图。

Author List Page

Author Detail Page

概要

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

在本文中,我们学习了如何使用泛型的基于类的列表和详细视图,并使用它们创建页面来查看我们的书籍和作者。一路上,我们学习了有关使用正则表达式进行模式匹配的知识,以及如何将数据从URL传递到视图。我们还学习了一些使用模板的技巧。最后我们展示了如何对列表视图进行分页,这样即使我们有很多记录,我们的列表也是可管理的。

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

See also

 

In this module

 

文档标签和贡献者

此页面的贡献者: xixilog, SphinxKnight
最后编辑者: xixilog,