通用视图把视图开发中常用的写法和模式抽象出来,让你编写少量代码就能快速实现常见的数据视图。显示对象列表就是这样一种任务
有了通用视图,可以把模型作为额外的参数传给 URL 配置。Django 自带的通用视图能实现下述功能:
• 列出对象并显示单个对象的详细信息。如果创建一个管理会议的应用程序,那么 TalkListView 和 Reg-
isteredUserListView 就是列表视图。某一个演讲的页面就是详细信息视图。
• 呈现基于日期的对象,显示为年月日归档页面(带有详细信息),以及“最新”页面。
• 让用户创建、更新和删除对象——需不需要授权都行
在视图中呈现数据库里的内容时最能体现 Django 的通用视图是多么强大。这是一件十分常见的任务,因此Django 内建了很多这方面的通用视图,不费吹灰之力就能生成对象列表和详细信息视图。
下面举几个例子。我们将使用下述模型:
# models.py
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
class Meta:
ordering = ["-name"]
def __str__(self):
return self.name
class Author(models.Model):
salutation = models.CharField(max_length=10)
name = models.CharField(max_length=200)
email = models.EmailField()
headshot = models.ImageField(upload_to='author_headshots')
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField('Author')
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
然后,定义一个视图:
# views.py
from django.views.generic import ListView
from books.models import Publisher
class PublisherList(ListView):
model = Publisher
最后,把视图与 URL 关联起来:
# urls.py
from django.conf.urls import url
from books.views import PublisherList
urlpatterns = [
url(r'^publishers/$', PublisherList.as_view()),
]
这就是我们需要编写的全部 Python 代码。当然,还要编写模板。我们可以为视图添加一个 template_name 属性,明确指明使用哪个模板;如果没明确指定,Django 将从对象的名称中推知。这里,推知的模板是 books/publisher_list.html ,其中“books”是模型所在应用的名称,“publisher”是模型名的小写形式。
因此,如果 TEMPLATES 设置中 DjangoTemplates 后台的 APP_DIRS 选项设为 True ,那么这个模板的位置是/path/to/project/books/templates/books/publisher_list.html 。
渲染这个模板时,上下文中有个名为 object_list 的变量,它的值是所有出版社对象。下面是一个十分简单的模板:
{% extends "base.html" %}
{% block content %}
Publishers
{% for publisher in object_list %}
- {{ publisher.name }}
{% endfor %}
{% endblock %}
你可能注意到了,上述出版社列表模板示例把所有出版社存储在一个名为 object_list 的变量中。这样虽然可行,但是对模板编写人不够友好,他们想知道的是处理的是出版社。
如果处理的是模型对象,Django 已经为你提供了这样一个变量。处理对象或查询集合时,Django 将向上下文中添加一个以模型类名小写形式命名的变量。这个变量与 object_list 同时存在,不过所含的数据完全相同。这里,变量名为 publisher_list 。
如果这还不够,可以自行设定上下文变量的名称。通用视图的 context_object_name 属性用于指定要使用的上下文变量名:
# views.py
from django.views.generic import ListView
from books.models import Publisher
class PublisherList(ListView):
model = Publisher
context_object_name = 'my_favorite_publishers'
为 context_object_name 设定一个友好的值总是好的,设计模板的同事会感谢你的。
通常,除了通用视图提供的信息之外,还想显示一些额外信息。例如,在各个出版社的详细信息页面显示出版的图书列表。 DetailView 通用视图在上下文中提供了出版社信息,但是如何在模板中获取额外的信息呢?答案是扩展 DetailView ,自己实现 get_context_data 方法。默认的实现只为模板提供该显示的对象,不过可以覆盖,提供更多信息:
from django.views.generic import DetailView
from books.models import Publisher, Book
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
# 先调用原来的实现,获取上下文
context = super(PublisherDetail, self).get_context_data(**kwargs)
# 把所有图书构成的查询集合添加到上下文中
context['book_list'] = Book.objects.all()
return context
如果想呈现特定出版社出版的图书列表,也可以使用这个技术:
from django.views.generic import ListView
from books.models import Book
class AcmeBookList(ListView):
context_object_name = 'book_list'
queryset = Book.objects.filter(publisher__name='Acme Publishing')
template_name = 'books/acme_list.html'
另一个常见的需求是根据 URL 中指定的键过滤列表页面中的对象。前面,我们在 URL 配置中硬编码出版社的名称,但是如果我们想编写一个视图显示随意一家出版社旗下的所有图书呢?
这也很方便,我们可以覆盖 ListView 的 get_queryset() 方法。它的默认实现是返回 queryset 属性的值,不过我们可以添加更多逻辑。这里的关键是,调用基于类的视图时,很多有用的东西存储到 self 中了,除了请求( self.request )之外,还有根据 URL 配置捕获的位置参数( self.args )和关键字参数( self.kwargs )。
下述 URL 配置只有一个捕获组:
# urls.py
from django.conf.urls import url
from books.views import PublisherBookList
urlpatterns = [
url(r'^books/([\w-]+)/$', PublisherBookList.as_view()),
]
然后,编写 PublisherBookList 视图类:
# views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher
class PublisherBookList(ListView):
template_name = 'books/books_by_publisher.html'
def get_queryset(self):
self.publisher = get_object_or_404(Publisher, name=self.args[0])
return Book.objects.filter(publisher=self.publisher)
可以看出,为查询集合添加逻辑还是相当容易的。如果需要,可以使用 self.request.user 通过当前用户过滤,或者实现其他更复杂的逻辑。与此同时,我们还可以把出版社对象添加到上下文中,供模板使用:
# ...
def get_context_data(self, **kwargs):
# 先调用原来的实现,获取上下文
context = super(PublisherBookList, self).get_context_data(**kwargs)
# 添加出版社对象
context['publisher'] = self.publisher
return context ## 执行额外的操作