简介
编写Web程序可能是很单调的,因为视图层,模板层,模型层都可能会有大量逻辑类似的代码,但是我们却需要不断的重复编写它们,这肯定让人受不了。为此,Django在视图层,对经常要编写的,逻辑类似的事务代码进行了抽象,并封装在Django中,这就是内建类视图,之后我们就可以利用内建类视图来进行高效开发。
这些通用的内建类视图可以处理以下事务逻辑:
- 表示某个对象的列表页面和详情页面。比如博客网站的博文的列表页面和就某一篇博文的详情页面。
- 基于时期或其它数据对某一类对象进行排序并呈现出来。
- 给予用户创建、更新、删除某类对象的权限。
以上这些事务基本上是开发人员所遇到的最常见的几类视图。
另外要注意的是,Django的内建类视图虽然很有用,但并不能为我们解决所有问题,使用它们的最常见的一种方法是用我们的类来继承这些内建类视图,然后根据具体问题决定如何去拓展。
使用ListView
以Django内置的ListView为例,讲解如何继承并使用它。
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, on_delete=models.CASCADE)
publication_date = models.DateField()
views.py:
from django.views.generic import ListView
from books.models import Publisher
class PublisherList(ListView):
model = Publisher
urls.py:
from django.urls import path
from books.views import PublisherList
urlpatterns = [
path('publishers/', PublisherList.as_view()),
]
以上是使用ListView的一个简单示例,我们没有直接使用它,而是使用它的子类PublisherList。ListView主要用于显示数据库中的内容,因此它具有一个很显而易见的属性:model,model用于指定该视图所基于的模型。
template_name
可以看到视图层部分的代码十分简单,ListView还有一个属性:template_name,表示用于呈现前端界面的模板,但在这里没有指定。需要注意的是,如果我们没有指定template_name,那么Django将会自动推断出一个template_name出来,在本例中,它将会推断出template_name为 books/publisher_list.html,其中books为该app的名字,publisher为模型名的小写。
另外,让我们想一想,如果我们使用函数视图来实现这一步功能的话,我们还需要传递一个参数字典过去,但是ListView怎么完成这一步呢?
默认情况下,ListVIew会传递一个object_list到模板层,因此这个模板可能是这样子的:
{% extends "base.html" %}
{% block content %}
<h2>Publishers</h2>
<ul>
{% for publisher in object_list %}
<li>{{ publisher.name }}</li>
{% endfor %}
</ul>
{% endblock %}
context_object_name
看起来挺好的,但是对于开发前后端分离的项目来说,你向前端传递了一个object_list,前端怎么知道这个object_list到底是哪个object?因此你最好在类视图中定义如下属性:context_object_name,该属性会指定object_list的新名字。
可能如下所示:
views.py:
from django.views.generic import ListView
from books.models import Publisher
class PublisherList(ListView):
model = Publisher
context_object_name = 'my_favorite_publishers'
DetailView
好了,现在又有新的需求来了。
如果我们想在每一个Publisher的详情页面展示一个书籍的列表,那么该怎么做呢?
还是先思考一下传统思路。如果用视图函数来完成这个功能的话,我们只需要在调用detail.html页面时把book_list传进去就好了,如果用内建视图的话,这需要用到DetailView。
如下:
from django.views.generic import DetailView
from books.models import Book, Publisher
class PublisherDetail(DetailView):
model = Publisher
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['book_list'] = Book.objects.all()
return context
默认情况下,get_context_data()需要一个self.object参数,但是你可以在你的子类中重写该方法以让它携带更多的数据。在本例中,self.object自然是publisher,但是我们还需要书籍列表,因此重写了它以使它携带了book_list这一参数。
queryset
在上面的例子中,我们用model参数来指定视图将要处理的数据模型,该参数可使用于需要处理单一对象或对象集合的所有视图。但是,除了model参数之外,其实还有类似的参数提供和model等同的功能,即queryset。
示例:
from django.views.generic import DetailView
from books.models import Publisher
class PublisherDetail(DetailView):
context_object_name = 'publisher'
queryset = Publisher.objects.all()
事实上,指定model = Publisher其实就是指定了queryset = Publisher.objects.all(),但是通过使用queryset,我们可以对objects进行进一步的筛选,比如:
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'
动态过滤
你可能注意到,上面的代码有一个很明显的问题,publisher__name被硬指定为ACME Publishing,但其实我们想要的是对于任意的publisher,我们都能得到正确的book_list,这又该怎么做呢?
所幸ListView提供有get_queryset()方法,它返回queryset,我们可以覆写它以便在get_queryset()中添加更多的逻辑。
使用此方法的关键是需要知道,从urlconf来的所有参数,包括request,传入类视图中后都会被存储在self中,比如self.request、self.args、self.kwargs。
示例如下:
urls.py
from django.urls import path
from books.views import PublisherBookList
urlpatterns = [
path('books/<publisher>/', PublisherBookList.as_view()),
]
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.kwargs['publisher'])
return Book.objects.filter(publisher=self.publisher)
如果我们愿意,我们还可以将publisher的信息添加到上下文中,如下:
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.kwargs['publisher'])
return Book.objects.filter(publisher=self.publisher)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['publisher'] = self.publisher
return context
关于内建类视图,这里先说这么多,事实上,Django的内建类视图提供有丰富的功能,关于它们更详尽的介绍将在之后给出。 二十七º 发布了96 篇原创文章 · 获赞 50 · 访问量 9324 私信 关注