zoukankan      html  css  js  c++  java
  • 转: django class base view 简单说明

    这节我们讲一下Class-based View,为什么要有这个Class-based View呢?view不都是一个方法吗?跟类有啥关系?其实答案很明显,用类其实是为了抽象,抽象出通用的,将可变的暴露出来,这样我们就可以用最少的代码实现复杂的功能了。

    Django中,对那些我们平时经常用的View进行了封装,比如用于渲染一个template的TemplateView,用于处理重定向的RedirectView,用于处理表单的FormView,用于处理数据库对象的DetailView和ListView等,这些View有一个共同的父类:View,在这个View类中,向外暴露了一个类方法:as_view(),它返回一个方法,这就是所有的View类的入口,这也和view是一个方法的说法不违背了。

    下面我们就分别来看下Django内置的几个常用的View是怎么实现,以及怎么使用的。

    TemplateView

    下面是TemplateView的实现类图:

    class-based view class diagram

    View类提供了as_view()类方法,注意,这个方法只能当作类方法使用,不能用在实例上,它返回一个内方法view(),在这个方法中,做的事情就是dispatch的事情,根据请求的方法,调用相应的方法去处理请求,如果你发送了一个GET请求,那么在view()方法中就会分发到get()方法中去处理。View类就主要封装了这个功能,这个功能是最高层的抽象,所有的view都需要有这个特性。

    ContextMixin类则只是实现了一个方法,get_context_data(),这是为在渲染template的时候,提供了一个默认的context,一般子类都会重写这个方法的。

    TemplateResponseMixin类,就是真正干事的类了,它在render_to_response()方法中,返回一个TemplateResponse对象,template用的就是类属性template_name指定的。

    然后TemplateView继承上面的三个类,实现了get()方法,

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)                                                                                                        
        return self.render_to_response(context)

    组织了一下父类中的方法,得到context,然后传给render_to_response()去构造TemplateResponse对象,渲染模板,返回,就完事了。

    以上,是Django给我们封装的,那么我们要怎么来用呢?其实,非常简单:

    最简单的例子:

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

    使用Class-based View有两种方法,一个是在as_view()中直接传入参数,它会覆盖掉该View原有的属性,这种情况只适用于处理不复杂的情况,另外一个就是直接继承该View,然后覆盖其中的方法,属性等,实现自己想要的功能,如:

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

    这样,在URLconf中,直接使用as_view()就可以了,不用传递参数:

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

    是不是很简单?只需要几行代码,就实现了以前要很多代码才能实现的功能,这就是框架的力量啊。

    ListView and DetailView

    ListView和DetailView可以放到一起来说,这两个View类实现的非常的相似,下面两个类图,分别是ListView和DetailView的类图,从类图上看,就可以知道它们是多么的像,所不同的就只是把获取一个对象列表的函数,换成了获取一个对象的函数,大同小异。我对这些View类灵活的运用,就必须清楚它的内部结构,才能知道怎么去实现自定制。

    ListView类图:

    class diagram of ListView

    DetailView类图:

    DetailView DTD

    在这两个类图中,最关键的组件就是MultiObjectMixin和SingleObjectMixin这两个类了,他们实现的功能是从数据库中读取数据,并且构建要传入template的context。每个类都有一些属性和方法可以覆盖,实现自定制,比如可以覆盖context_object_name变量,用来指定传入template的context的对象的变量名;可以覆盖get_context_data()方法,用来将其他的变量放到context中;为queryset赋值,就可以自己指定这个View操作的对象(列表);或者是直接重写get_queryset()/get_object()方法,简单暴力。

    注意,这两个类,也是继承自TemplateResponseMixin,也就是说它们也是直接返回的TemplateResponse对象。

    好,我们来举一个简单的例子:

    persons/urls.py

    from django.conf.urls import patterns, include, url 
    from persons.views import PersonListView, PersonDetailView
    
    urlpatterns = patterns('',
        url(r'^persons/$', PersonListView.as_view(), name='list'),
        url(r'^persons/(?P<pk>d+)/$', PersonDetailView.as_view(), name='detail'),                                                                               
    )

    persons/views.py

    from persons.models import Person
    from django.views.generic import ListView, DetailView
    
    class PersonListView(ListView):
        model = Person
        context_object_name = 'persons'
    
    class PersonDetailView(DetailView):
        model = Person

    persons/templates/persons/person_list.html

    <h2>Person List</h2>                                                                                                                                         
    
    <ul>
      {% for person in persons %}
      <li><a href={% url detail person.id %}>{{ person.first_name }} {{ person.last_name }}</a></li>
      {% endfor %}
    </ul>

    persons/templates/persons/person_detail.html

    <h2>Person Detail</h2>                                                                                                                                       
    
    {{ object.first_name }} {{ object.last_name }}

    可以看到两个View类只有简简单单几行代码,覆盖了几个属性,就把整个view的功能完成了,你可能要问几个问题:

    1. context变量是什么呢?
    2. template是哪个呢?
    3. DetailView的URLconf的Pattern该怎么写?一定要写pk吗?

    呵呵,这就是约定的力量,我只能说一切都在源码中,看文档都不一定能彻底明白。

    Refs

    附录:

    ListView类图代码:

    @startuml
    
    class ContextMixin{
        get_context_data(self, **kwargs)
    }
    
    class View{
        {abstract} as_view(cls, **initkwargs)
        dispatch(self, request, *args, **kwargs)
    }
    
    class TemplateResponseMixin{
        template_name = None
        response_class = TemplateResponse
        content_type = None
    
        render_to_response(self, context, **response_kwargs)
        get_template_names(self)
    }
    
    class MultipleObjectMixin{
        allow_empty = True
        queryset = None
        model = None
        paginate_by = None
        paginate_orphans = 0
        context_object_name = None
        paginator_class = Paginator
        page_kwarg = 'page'
        ordering = None
    
        get_queryset()
        get_context_object_name(self, object_list)
        get_context_data(self, **kwargs)
    }
    
    class BaseListView{
        get(self, request, *args, **kwargs)
    }
    
    class MultipleObjectTemplateResponseMixin{
        template_name_suffix = '_list'
        get_template_names()
    }
    
    ContextMixin <|-- MultipleObjectMixin
    MultipleObjectMixin <|-- BaseListView
    View <|-- BaseListView
    TemplateResponseMixin <|-- MultipleObjectTemplateResponseMixin
    MultipleObjectTemplateResponseMixin <|-- ListView
    BaseListView <|-- ListView
    
    @enduml

    DetailView类图代码:

    @startuml
    
    class ContextMixin{
        get_context_data(self, **kwargs)
    }
    
    class View{
        {abstract} as_view(cls, **initkwargs)
        dispatch(self, request, *args, **kwargs)
    }
    
    class TemplateResponseMixin{
        template_name = None
        response_class = TemplateResponse
        content_type = None
    
        render_to_response(self, context, **response_kwargs)
        get_template_names(self)
    }
    
    class SingleObjectMixin{
        queryset = None
        model = None
        slug_field = 'slug'
        context_object_name = None
        slug_url_kwarg = 'slug'
        pk_url_kwarg = 'pk'
        query_pk_and_slug = False
    
        get_object(self, queryset=None)
        get_queryset(self)
        get_context_object_name(self, object_list)
        get_context_data(self, **kwargs)
    }
    
    class BaseDetailView{
        get(self, request, *args, **kwargs)
    }
    
    class SingleObjectTemplateResponseMixin{
        template_name_suffix = '_detail'
        template_name_field = None
    
        get_template_names()
    }
    
    ContextMixin <|-- SingleObjectMixin
    SingleObjectMixin <|-- BaseDetailView
    View <|-- BaseDetailView
    TemplateResponseMixin <|-- SingleObjectTemplateResponseMixin
    SingleObjectTemplateResponseMixin <|-- DetailView
    BaseDetailView <|-- DetailView
    
    @enduml
  • 相关阅读:
    如何防止表单重复提交
    二、编程题
    Java 基础测试题
    常见异常
    Hibernate工作原理及为什么要用?
    简述拦截器的工作原理以及你在项目中使用过哪些自定义拦截器。
    拦截器和过滤器的区别
    浏览器默认样式
    数组去重
    数组排序
  • 原文地址:https://www.cnblogs.com/ohgenlong/p/7298913.html
Copyright © 2011-2022 走看看