Generic Views

本文介绍了Django通用视图的使用方法,包括如何减轻Web开发中的重复劳动,通过示例展示了列表视图和详情视图的配置过程,并探讨了自定义视图以满足特定需求的技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:https://djangobook.com/generic-views/

Generic Views

Here again is a recurring theme of this book: at its worst, Web development is boring and monotonous. So far, we’ve covered how Django tries to take away some of that monotony at the model and template layers, but Web developers also experience this boredom at the view level.
这里是这本书反复出现的主题:在最坏的情况下,我们的开发工作是无聊和单调的。迄今为止,我们已经尝试过如何去消除模型和模板层的冗余,但是Web开发者在视图层中同样会感到厌烦。

Django’s generic views were developed to ease that pain.
Django的通用视图被开发用来解决这种痛苦。

They take certain common idioms and patterns found in view development and abstract them so that you can quickly write common views of data without having to write too much code. We can recognize certain common tasks, like displaying a list of objects, and write code that displays a list of any object.
它们采用视图开发中确定的通用风格和模式,并且对他们进行抽象,以至于你可以快速写出数据的通用视图,而不必编写大量的代码。我们可以识别确定的共同的任务,像显示对象列表,和编写显示对象列表的代码。

Then the model in question can be passed as an extra argument to the URLconf. Django ships with generic display views to do the following:
然后,问题中的模型可以被作为额外参数传递给URFconf.附带通用视图的Django做如下操作:

  • Display list and detail pages for a single object. If we were creating an application to manage conferences, then a TalkListView and a RegisteredUserListView would be examples of list views. A single talk page is an example of what we call a “detail” view.
    为一个对象显示列表和细节页面。如果我们创建一个应用来管理会议,这是,一个TalkListView和RegisteredUserListView将成为列表视图的例子。一个单独的讨论页面是细节视图的一个例子。

  • Present date-based objects in year/month/day archive pages, associated detail, and “latest” pages.
    显示年/月/日页面中基于数据的对象,相关细节和最新页面。

  • Allow users to create, update, and delete objects – with or without authorization.
    Taken together, these views provide easy interfaces to perform the most common tasks developers encounter when displaying database data in views. Finally, display views are only one part of Django’s comprehensive class-based view system. For a full introduction and detailed description of the other class-based views Django provides, see Appendix C.
    允许用户创建,更新和删除对象 - 有或没有授权。合起来,这些视图提供简单的接口来实现大多数普通的任务给开发者们来面对在视图中显示数据库数据的情况。最后,显示视图只是Django全部基于类视图系统中的一部分。对于Django提供的其他类基视图的完整介绍和细节描述,请看附录C。

Generic Views of Objects

通用的对象视图

Django’s generic views really shine when it comes to presenting views of your database content. Because it’s such a common task, Django comes with a handful of built-in generic views that make generating list and detail views of objects incredibly easy.
Django的通用视图在展示数据库内容视图时确实很亮眼。因为它是一个很常见的任务,Django带有一些内建通用视图让生成对象的列表和细节视图非常的容易。

Let’s start by looking at some examples of showing a list of objects or an individual object. We’ll be using these models:
让我们从显示对象列表或一个对象的例子开始。我们将使用这些模型:

# 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()

Now we need to define a view:
现在我们需要定义一个视图:

# views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
    model = Publisher

Finally hook that view into your urls:
最后把视图挂在你的url中:

# urls.py
from django.conf.urls import url
from books.views import PublisherList

urlpatterns = [
    url(r'^publishers/$', PublisherList.as_view()),
]

That’s all the Python code we need to write. We still need to write a template, however. We could explicitly tell the view which template to use by adding a template_name attribute to the view, but in the absence of an explicit template Django will infer one from the object’s name. In this case, the inferred template will be books/publisher_list.html – the “books” part comes from the name of the app that defines the model, while the “publisher” bit is just the lowercased version of the model’s name.
这就是我们需要写的全部的Python代码。然而,我们仍然需要写一个模板。我们可以明确的告诉视图用哪一个模板通过添加template_name属性给视图,但是在缺少明确的模板时Django将推测一个对象的名字。在这个例子中,被推测的模板是books/publisher_list.html - “book”来自定义模块的应用名,而“publisher”只是模块名的小写版本。

Thus, when (for example) the APP_DIRS option of a DjangoTemplates backend is set to True in TEMPLATES, a template location could be: /path/to/project/books/templates/books/publisher_list.html
因此,当(例如)DjangoTemplates后端的APP_DIRS选项在TEMPLATES被正确设置,一个模板位置应该是:/path/to/project/books/templates/books/publisher_list.html

This template will be rendered against a context containing a variable called object_list that contains all the publisher objects. A very simple template might look like the following:
这个模板被叫做一个包含叫做object_list的变量渲染,它包含全部的publisher对象。一个很简单的模板可能像下面这样:

{% extends "base.html" %}

{% block content %}
    <h2>Publishers</h2>
    <ul>
        {% for publisher in object_list %}
            <li>{{ publisher.name }}</li>
        {% endfor %}
    </ul>
{% endblock %}

That’s really all there is to it. All the cool features of generic views come from changing the attributes set on the generic view. Appendix C documents all the generic views and their options in detail; the rest of this document will consider some of the common ways you might customize and extend generic views.
这就是全部了。所有炫酷的通用视图的特性都来自通用视图改变属性集上。附录C记录了所有的通用视图和他们的细节选项;文档剩余部分将被看做一些常用方法,你可能用来定制或扩展通用视图。

Making “Friendly” Template Contexts

制作友好的模板内容

You might have noticed that our sample publisher list template stores all the publishers in a variable named object_list. While this works just fine, it isn’t all that “friendly” to template authors: they have to “just know” that they’re dealing with publishers here.
你可能已经注意到我们的例子发布人列表模板,存储全部发布者在一个叫做object_list的变量中。尽管它能够工作,但是它对模板作者不是很友好:他们只知道这里需要处理发布者。

In Django, if you’re dealing with a model object, this is already done for you. When you are dealing with an object or queryset, Django populates the context using the lower cased version of the model class’ name. This is provided in addition to the default object_list entry, but contains exactly the same data, i.e. publisher_list.
在Django中,如果你正在处理模型对象,它已经为你做了。当你在处理对象或查询集合,Django使用模块类名的小写版本填充内容。它被提供一个额外的默认object_list入口,但是包含和publisher_list相同的数据。

If this still isn’t a good match, you can manually set the name of the context variable. The context_object_name attribute on a generic view specifies the context variable to use:
如果这不是好的处理方式,你可以手动地设置内容变量的名字。通用视图中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'

Providing a useful context_object_name is always a good idea. Your co-workers who design templates will thank you.
提供一个有用的context_object_name通常是一个好主意。你的设计末班的同事将会感激你。

Adding Extra Context

添加额外的内容

Often you simply need to present some extra information beyond that provided by the generic view. For example, think of showing a list of all the books on each publisher detail page. The DetailView generic view provides the publisher to the context, but how do we get additional information in that template?
通常你需要在通用视图上需要简单的展现一些额外的数据。例如,想在每一个出版商详细页面上显示全部书籍列表。这个DetailView通用视图提供上下文给出版商,但是我们如何在末班中拿到数据呢?

The answer is to subclass DetailView and provide your own implementation of the get_context_data method. The default implementation simply adds the object being displayed to the template, but you can override it to send more:
答案是继承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):
        # Call the base implementation first to get a context
        context = super(PublisherDetail, self).get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        context['book_list'] = Book.objects.all()
        return context

Generally, get_context_data will merge the context data of all parent classes with those of the current class. To preserve this behavior in your own classes where you want to alter the context, you should be sure to call get_context_data on the super class. When no two classes try to define the same key, this will give the expected results.However, if any class attempts to override a key after parent classes have set it (after the call to super), any children of that class will also need to explicitly set it after super if they want to be sure to override all parents. If you’re having trouble, review the method resolution order of your view.
通常,get_context_data将合并当前类全部父类的上下文数据。在你改变数据的地方,为了保护你类中的行为,你应该确保调用父类中的get_context_data。当没有两个类定义相同的键,它将给出期待的结果。然而,如果任何类尝试在超类调用之后重写键值,任何子类将需要明确的在超类之后设置它,如果它们想确保重写全部父类。如果有问题,检查视图中方法解析顺序。

Viewing Subsets of Objects

查看对象的子集

Now let’s take a closer look at the model argument we’ve been using all along. The model argument, which specifies the database model that the view will operate upon, is available on all the generic views that operate on a single object or a collection of objects. However, the model argument is not the only way to specify the objects that the view will operate upon – you can also specify the list of objects using the queryset argument:
现在让我们近距离的查看我们已经使用的模型参数。将被改进的数据库模型的模型参数,可用于所有的视图模型来操作单一对象或者对象的集合。然而,模型参数不是唯一的方式来指定要改进视图的对象 - 你也可以使用queryset参数指定对象列表

from django.views.generic import DetailView
from books.models import Publisher

class PublisherDetail(DetailView):

    context_object_name = 'publisher'
    queryset = Publisher.objects.all()

Specifying model = Publisher is really just shorthand for saying queryset = Publisher.objects.all(). However, by using queryset to define a filtered list of objects you can be more specific about the objects that will be visible in the view. To pick a simple example, we might want to order a list of books by publication date, with the most recent first:
指定model = Publisher确实是说明queryset = Publisher.objects.all()的一种快捷方法。然而,使用queryset定义过滤的对象列表,你可以更具体地在视图中显示对象。举个简单的例子,你可能想根据出版日期对书籍列表排序,最近的优先显示。

from django.views.generic import ListView
from books.models import Book

class BookList(ListView):
    queryset = Book.objects.order_by('-publication_date')
    context_object_name = 'book_list'

That’s a pretty simple example, but it illustrates the idea nicely. Of course, you’ll usually want to do more than just reorder objects. If you want to present a list of books by a particular publisher, you can use the same technique:
很简单,但是很漂亮的阐明了想法。当然,你可能想做比重排序更多的事情。如果你想显示特定出版商的图书,你可以使用这样的技术:

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'

Notice that along with a filtered queryset, we’re also using a custom template name. If we didn’t, the generic view would use the same template as the “vanilla” object list, which might not be what we want.
注意伴随过滤的queryset,我们同样使用定制的模板名。如果不这样,通用视图使用模板名如“vanilla”的对象列表,可能不是我们想要的。

Also notice that this isn’t a very elegant way of doing publisher-specific books. If we want to add another publisher page, we’d need another handful of lines in the URLconf, and more than a few publishers would get unreasonable. We’ll deal with this problem in the next section.
同样注意到这不是一个优雅的办法来完成指定出版商获取图书这件事。如果我们想添加另一个出版商页面,我们需要另外几行URLconf,并且超过几个出版商就不合理了。接下来我们解决这个问题。

Dynamic Filtering

动态过滤

Another common need is to filter down the objects given in a list page by some key in the URL. Earlier we hard-coded the publisher’s name in the URLconf, but what if we wanted to write a view that displayed all the books by some arbitrary publisher?
另一个常见的需求是根据URL关键字过滤列表中的对象。之前,我们将出版商名字硬编码到URLconf中,但是如果我们想写一个页面显示一些出版商的所有图书怎么办?

Handily, the ListView has a get_queryset() method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic. The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request), this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.
方便的是,我们可以重写ListView的get_queryset()方法。之前,它返回queryset属性的值,但是,现在我们添加一些魔法。工作的关键部分当类基视图被调用,它本身存储着许多有用的东西;请求也一样,它包括根据URLconf捕获的未知参数和命名参数。

Here, we have a URLconf with a single captured group:
这里,我们有一个带有单个捕获组的URLconf

# urls.py
from django.conf.urls import url
from books.views import PublisherBookList

urlpatterns = [
    url(r'^books/([\w-]+)/$', PublisherBookList.as_view()),
]

Next, we’ll write the PublisherBookList view itself:
接下来,我们写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)

As you can see, it’s quite easy to add more logic to the queryset selection; if we wanted, we could use self.request.user to filter using the current user, or other more complex logic. We can also add the publisher into the context at the same time, so we can use it in the template:
如你所见,很容易添加更多的逻辑选择;如果我们想,我们可以使用self.request.user来过滤当前用户,或者其他的更复杂的逻辑。我们也可以同时把出版商添加到上下文,所以我们可以在末班中这样使用它:

# ...

def get_context_data(self, **kwargs):
    # Call the base implementation first to get a context
    context = super(PublisherBookList, self).get_context_data(**kwargs)

    # Add in the publisher
    context['publisher'] = self.publisher
    return context ## Performing Extra Work

The last common pattern we’ll look at involves doing some extra work before or after calling the generic view. Imagine we had a last_accessed field on our Author model that we were using to keep track of the last time anybody looked at that author:
我们来看最后一个模式,它是在调用通用视图之前或之后做一些额外的事情。想象一下我们的Author模型中有一个last_accessed字段,它用来追踪任何人最后看到的作者:

# models.py
from django.db import models

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')
    last_accessed = models.DateTimeField()

The generic DetailView class, of course, wouldn’t know anything about this field, but once again we could easily write a custom view to keep that field updated. First, we’d need to add an author detail bit in the URLconf to point to a custom view:
通用的DetailView类,当然,不需要知道这个字段的任何信息,但是再一次我们可以容易的写出定制视图保持字段更新。首先,我们需要在URLconf中添加作者信息指向定制的视图。

from django.conf.urls import url
from books.views import AuthorDetailView

urlpatterns = [
    #...
    url(r'^authors/(?P<pk>[0-9]+)/$', AuthorDetailView.as_view(), name='author-detail\
'),
]

Then we’d write our new view – get_object is the method that retrieves the object – so we simply override it and wrap the call:
然后,我们写新的视图 - get_object是一个检索对象的方法 - 我们简单重写它并包装调用

from django.views.generic import DetailView
from django.utils import timezone
from books.models import Author

class AuthorDetailView(DetailView):

    queryset = Author.objects.all()

    def get_object(self):
        # Call the superclass
        object = super(AuthorDetailView, self).get_object()

        # Record the last accessed date
        object.last_accessed = timezone.now()
        object.save()
        # Return the object
        return object

The URLconf here uses the named group pk – this name is the default name that DetailView uses to find the value of the primary key used to filter the queryset.
这里URLconf使用pk命名组 - 它是默认名字,DetailView用它查找主键的值,被用来过滤queryset。

If you want to call the group something else, you can set pk_url_kwarg on the view. More details can be found in the reference for DetailView.
如果你想调用组中其他一些东西,一个可以在视图中设置pk_url_kwarg。更多的细节参考DetailView。

What’s Next?
接下来是什么?

In this chapter we looked at only a couple of the generic views Django ships with, but the general ideas presented here apply pretty closely to any generic view. Appendix C covers all the available views in detail, and it’s recommended reading if you want to get the most out of this powerful feature.
本章我们只看到Django带来的一些通用视图,但是这些一般的想法在任何通用视图中应用都非常紧密。
附录C详细地包含了可用的视图,并且,如果你想要获得强大的特性,推荐你阅读它。

This concludes the section of this book devoted to advanced usage of models, templates and views. The following chapters cover a range of functions that are very common in modern commercial websites. We will start with a subject essential to building interactive websites – user management.
这些总结了本书中模型、模板和视图的高级使用。接下来的章节介绍现代商业网站常见的一些功能。我们将从一个主题开始建立交互网站 - 用户管理。

<<< Executing Raw SQL | Table of Contents | User Authentication in Django >>>
<<< 执行原始SQL | 目录 | Django中用户身份验证 >>>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值