13:基于类的通用视图

基于类的通用视图

前面我们说过了django的通用视图,不知道django通用视图的去看我前面的随笔,谢谢

django的通用视图帮我们省略了很多代码,但有时候django的通用视图并不能满足我们全部的需求,例如像重定义一些属性和方法的时候,或者我们想换种写法的时候,因此,django提供了基于类的通用视图,通过子类或者在url中传参的方法来配置我们的基于类的通用视图

通用视图和基于类的通用视图是两种不一样的写法,前面我们介绍的通用视图,所有的代码集中于url的配置文件中,而基于类的通用视图主要集中于配置“继承然后覆盖父类方法和属性”方面,下面是一个基于函数的视图和基于类的视图的对应关系

基于函数基于类
django.views.generic.simple.direct_to_template django.views.generic.base.TemplateView
django.views.generic.simple.redirect_to django.views.generic.base.RedirectView
django.views.generic.list_detail.object_list django.views.generic.list.ListView
django.views.generic.list_detail.object_detail django.views.generic.detail.DetailView
django.views.generic.create_update.create_object django.views.generic.edit.CreateView
django.views.generic.create_update.update_object django.views.generic.edit.UpdateView
django.views.generic.create_update.delete_object django.views.generic.edit.DeleteView
django.views.generic.date_based.archive_index django.views.generic.dates.ArchiveIndexView
django.views.generic.date_based.archive_year django.views.generic.dates.YearArchiveView
django.views.generic.date_based.archive_month django.views.generic.dates.MonthArchiveView
django.views.generic.date_based.archive_week django.views.generic.dates.WeekArchiveView
django.views.generic.date_based.archive_day django.views.generic.dates.DayArchiveView
django.views.generic.date_based.archive_today django.views.generic.dates.TodayArchiveView
django.views.generic.date_based.object_detail django.views.generic.dates.DateDetailView

简单用法

考虑你仅仅想要显示一个模板about.html,django提供了一个通用视图去做这件事,我们只需要继承它,然后覆盖覆盖模板名称即可,其实TemplateView类对应的是direct_to_template

# some_app/views.py
from django.views.generic import TemplateView
class AboutView(TemplateView):
    template_name = "about.html"

然后再url配置文件中直接调用相应的方法即可

# urls.py
from django.conf.urls import patterns, url, include
from some_app.views import AboutView
urlpatterns = patterns('',
    (r'^about/', AboutView.as_view()),
)

或者我们可以直接在url中通过传参的方法达到修改属性的目的

from django.conf.urls import patterns, url, include
from django.views.generic import TemplateView
urlpatterns = patterns('',
    (r'^about/', TemplateView.as_view(template_name="about.html")),
)

对象的通用视图

首先这是我们可能会用到的model

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

为了生成一个所有出版商的页面,我们可以在url中这样写

from django.conf.urls import patterns, url, include
from django.views.generic import ListView
from books.models import Publisher
urlpatterns = patterns('',
    (r'^publishers/$', ListView.as_view(
        model=Publisher,
    )),
)

在这里我们没有指定模板名,那么会使用ListView默认的模板名默认模板<app_label>/<model_name>_list.html ,这里可能是/books/publisher_list.html

拓展通用视图

制作友好的模板上下文名称

如果你在和模型打交道,使用下面的配置可以使你自己定义一个更好用(django内部有规则生成一个)的模板上下文名称,这可能使得和你合作的模板设计师对你有好感,因为有时候django自动生成的模板上下文名称很冗长

urlpatterns = patterns('',
    (r'^publishers/$', ListView.as_view(
        model=Publisher,
        context_object_name="publisher_list",
    )),
)

增加额外的上下文内容

有时候默认的通用视图提供的上下文内容不能满足你的需求,比如一个返回所有出版商的列表的页面,上面可能一点和书籍的信息都没有,这时候你可以通过继承相应的父类,覆盖父类的方法为子类添加额外的内容

from django.views.generic import DetailView
from books.models import Publisher, Book
class PublisherDetailView(DetailView):
    context_object_name = "publisher"
    model = Publisher
    def get_context_data(self, **kwargs):
        # Call the base implementation first to get a context
        context = super(PublisherDetailView, self).get_context_data(**kwargs)
        # Add in a QuerySet of all the books
        context['book_list'] = Book.objects.all()
        return context

对象的查看子集

注意到的一点,model属性和queryset属性,model=Publisher和queryset=Publisher.objects.all()基本是一致的,基于这一点,我们可以通过制定queryset的方法以至于不必每次都收返回一个模型的全集,可以每次只返回一个子集,当然,我们也可以在url中达到这个目标

urlpatterns = patterns('',
    (r'^publishers/$', ListView.as_view(
        queryset=Publisher.objects.all(),
        context_object_name="publisher_list",
    )),
    (r'^books/$', ListView.as_view(
        queryset=Book.objects.order_by("-publication_date"),
        context_object_name="book_list",
    )),
)
from django.views.generic import ListView
from books.models import Book
class AcmeBookListView(ListView):
    context_object_name = "book_list"
    queryset = Book.objects.filter(publisher__name="Acme Publishing")
    template_name = "books/acme_list.html"

动态筛选

还有一个常用的需求是根据url中的参数去查询数据库,之前我们可以把出版商的名字硬编码在url中,但如果我们要写一个能够根据任意给定的出版商,返回该出版商的所有书籍的视图呢?我们可以覆盖get_queryset()这个方法

    def get_queryset(self):
        publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
        return Book.objects.filter(publisher=publisher)

执行额外的工作

假如Author这个模型有一个last_accessed的date类型属性,我们要求每次访问的时候都更新这个属性,显然,django的通用视图是根本不知道Author有这样的一个属性的,所以我们必须重写某个方法以增加这个行为,在这里是get_object()方法

import datetime
from books.models import Author
from django.views.generic import DetailView
from django.shortcuts import get_object_or_404
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 = datetime.datetime.now()
        object.save()
        # Return the object
        return object

装饰基于类的视图

在url中装饰

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView
from .views import VoteView
urlpatterns = patterns('',
    (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
)

在类中装饰

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
    template_name = 'secret.html'
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectedView, self).dispatch(*args, **kwargs)

p

posted @ 2015-01-03 20:09  2BiTT  阅读(755)  评论(0编辑  收藏  举报