zoukankan      html  css  js  c++  java
  • Django Class Based View

    本节内容

    一   Class Based View 基于类的视图

      1.  类的视图 View

      2.  类的视图 TemplateView

      3.  类的视图 login_required解决方法

    二  通用视图

      1.  通用视图 - ListView

      2.  通用视图 - DetailView

      3.  通用视图 - FormView

    一  Class Based View 基于类的视图

    function view 存在问题,无法继承复用,尤其时框架封装好的类用不了,function组装复用更擅长

    class based view 代码更简洁

    简单样例

    version 1

    # urls.py
    from django.conf.urls import url
    from mysite import views as my_view
    
    urlpatterns = [
        url(r'^about/', my_view.about),
    ]
    
    # mysite/views.py
    from django.shortcuts import render
    
    def about(request):
        return render(request, 'about.html')

    基本的function based view

    version 2

    # urls.py
    from django.views.generic import TemplateView
    urlpatterns = [
        url(r'^about/', TemplateView.as_view(template_name='about.html')),
    ]
    
    # mysite/views.py
    from django.shortcuts import render
    
    def about(request):
        return render(request, 'about.html')

    TemplateView.as_view(template_name='about.html'))  方法 as_view(),参数template_name=‘网页模板’

    version 3

    # mysite/views.py
    from django.views.generic import TemplateView
    class AboutView(TemplateView):
        template_name = 'about.html'
    
    #mysite/urls.py
    from mysite import views as my_view
    
    urlpatterns = [
        url(r'^about/', my_view.AboutView.as_view()),
    ]

    说明:

    • as_view()返回的是一个function object

    • 模板名字作为as_view参数传进去,也可以作为类变量设置template_name = '网页模板'

    1.  类的视图 View

    # mysite/views.py
    def
    my_view(request): if request.method == 'GET': return render(request, 'about.html') elif request.method == 'POST': return HttpResponse('post it') elif request.method == 'HEAD': return HttpResponse('head it')
    # mysite/urls.py
    from mysite import views
    urlpatterns = [
        url(r'^about/', views.my_view),
    ]

    等价于 类图公共基类View

    # mysite/views.py
    from django.shortcuts import render,HttpResponse
    from django.views.generic import View
    
    class MyView(View):
        def get(self, request):
            return render(request, 'about.html')
    
        def post(self, request):
            return HttpResponse('post it')
    
        def head(self, request):
            return HttpResponse('head it')
    
    # mysite/urls.py
    from mysite import views as my_view
    urlpatterns = [
        url(r'^about/', my_view.MyView.as_view()),
    ]

    类视图好处就是可以直接继承和覆盖

    # mysite/views.py
    from django.views.generic import View
    class GreetingView(View):
        greeting = 'Good Day'
        def get(self, request):
            return HttpResponse(self.greeting)
    
    class MorningGreeting(GreetingView):
        greeting = 'Morning to ya'
    
    # mysite/urls.py
    urlpatterns = [
    my_view.GreetingView.as_view(greeting="G'day")),
    ]

    源码分析

    class View(object):
        # 所有views的公共类,仅仅实现 dispatch方法和简单安全性验证
    
        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
        def __init__(self, **kwargs):
            for key, value in six.iteritems(kwargs):
                setattr(self, key, value)
    
        @classonlymethod
        def as_view(cls, **initkwargs):
            # request-response的主入口点
            
            for key in initkwargs:
                # 不能有get,post等方法名的参数,后续有setattr()操作,会覆盖原始请求方法
                if key in cls.http_method_names:
                    raise TypeError("You tried to pass in the %s method name as a "
                                    "keyword argument to %s(). Don't do that."
                                    % (key, cls.__name__))
    
                # 如果传入了一个没有定义的类属性,就报错;如有greeting属性,就不报错
                if not hasattr(cls, key):
                    raise TypeError("%s() received an invalid keyword %r. as_view "
                                    "only accepts arguments that are already "
                                    "attributes of the class." % (cls.__name__, key))
    
            # 定义了类似闭包的视图函数
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)  # 调用__init__()进行实例化
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs) # 调用 self.dispatch()进行处理
    
            # 对view object设置了一些属性
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            # 更新文档说明
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
            return view
    
        def dispatch(self, request, *args, **kwargs):
            # 将request.method反射到类相应的方法上,并执行
            if request.method.lower() in self.http_method_names:
                # 如果有如get,就handler=get;如果没有handler = self.http_method_not_allowed
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)
    
        def http_method_not_allowed(self, request, *args, **kwargs):
            logger.warning(
                'Method Not Allowed (%s): %s', request.method, request.path,
                extra={'status_code': 405, 'request': request}
            )
            return http.HttpResponseNotAllowed(self._allowed_methods())
    
        def options(self, request, *args, **kwargs):
            """
            Handles responding to requests for the OPTIONS HTTP verb.
            """
            response = http.HttpResponse()
            response['Allow'] = ', '.join(self._allowed_methods())
            response['Content-Length'] = '0'
            return response
    
        def _allowed_methods(self):
            return [m.upper() for m in self.http_method_names if hasattr(self, m)]
    类View
    #!/usr/bin/env python3
    # -*-coding:utf-8 -*-
    # __author__:Jonathan
    # email:nining1314@gmail.com
    
    from django.shortcuts import HttpResponse
    from django import http
    from django.utils import six
    from django.utils.decorators import classonlymethod
    from django.conf.urls import url
    
    class View(object):
        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
        def __init__(self, **kwargs):
            for key, value in six.iteritems(kwargs):
                setattr(self, key, value)
    
        @classonlymethod
        def as_view(cls, **initkwargs):
            for key in initkwargs:
                if key in cls.http_method_names:
                    raise TypeError("禁止 %s 作为 %s()关键字参数" % (key, cls.__name__))
                if not hasattr(cls, key):
                    raise TypeError("类 %s() 接收到非法参数 %s,只接受类存在类属性" % (cls.__name__, key))
            
            def view(request, *args, **kwargs):
                # 把 as_view()传入的关键字参数,赋值为相应的类变量
                self = cls(**initkwargs)  # 类的实例化,虽然只是一行代码,要理解原理:是有很多行代码执行完的结果
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)  # 一行代码,代表的是self.dispatch()的执行结果
            
            # 函数也是一种对象,可以有自己的属性****
            view.view_class = cls
            view.view_initkwargs = initkwargs
            
            # 返回闭合的函数对象
            return view
        
        def dispatch(self, request, *args, **kwargs):
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)   # 一行代码,代表的是handler()的执行结果
        """最终dispatch结果:根据request.method.lower()得到相应的http请求方法,反射到同名类方法,执行"""
            
        def http_method_not_allowed(self, request, *args, **kwargs):
            return http.HttpResponseNotAllowed(self._allowed_methods())
        
        def _allowed_methods(self):
            return [m.upper() for m in self.http_method_names if hasattr(self, m)]
    
    
    class MyView(View):
        def get(self, request):
            return HttpResponse('get it')
    
        def post(self, request):
            return HttpResponse('post it')
    
        def head(self, request):
            return HttpResponse('head it')
    
    urlpatterns = [
        url(r'^my_view/', MyView.as_view()),
    ]
    吃透类View

    说明: 

    • as_view返回一个 view function 

    • as_view接收参数是可以覆盖类定义的变量

    • __init__检查as_view传入的参数是否在类中定义

    • View.as_view()返回函数视图的view 对象,说明类视图只是对原有的函数视图进行了封装,而没有否定函数view的作用

    • view function运行时会调用dispatch,根据用户的request.method路由到get, post等方法

    最终得出结论:

    View类 在没有更改原Django逻辑的情况下,可以用类来编写view,每个http请求会使用对应类的同名方法进行处理

    2.  类的视图 TemplateView

    class ContextMixin(object):
        # 渲染模板时,处理需要的上下文参数
    
        def get_context_data(self, **kwargs):
            if 'view' not in kwargs:
                kwargs['view'] = self
            return kwargs
    
    class TemplateResponseMixin(object):
        # 渲染模板,Mixin一般是小功能类封装,被其他类继承
    
        template_name = None
        template_engine = None
        response_class = TemplateResponse
        content_type = None
    
        def render_to_response(self, context, **response_kwargs):
            # 进行模板渲染,render()是个函数,response_class()是个类,实现本质目标没多大区别
    
            response_kwargs.setdefault('content_type', self.content_type)
            return self.response_class(
                request=self.request,
                template=self.get_template_names(),
                context=context,
                using=self.template_engine,
                **response_kwargs
            )
    
        def get_template_names(self):
            if self.template_name is None:
                raise ImproperlyConfigured(
                    "TemplateResponseMixin requires either a definition of "
                    "'template_name' or an implementation of 'get_template_names()'")
            else:
                return [self.template_name]
    
    
    class TemplateView(TemplateResponseMixin, ContextMixin, View):
        # 实现get()方法,有渲染上文参数,有渲染操作
    
        def get(self, request, *args, **kwargs):
            context = self.get_context_data(**kwargs)
            return self.render_to_response(context)
    类TemplateView

    说明:

    • 每个Mixin只提供部分功能,最终需要类整合

    • TemplateResponseMixin提供render_to_response() 方法,渲染模板

    • ContentMixin提供get_context_data()方法,提供渲染数据

    • View提供get,post用户访问接口

    • 面向对象编程:如何拆分功能模块,如何组装功能模块

     

    3.  类的视图 login_required解决方法

    3.1  封装Mixin(推荐)

    from django.contrib.auth.decorators import login_required
    
    class LoginRequiredMixin(object):
        @classmethod
        def as_view(cls, **initkwargs):
            view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
            return login_required(view)
    
    class MyView(LoginRequiredMixin, ...):
        pass

    3.2 装饰类

    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, request, *args, **kwargs):
            return super(ProtectedView, self).dispatch(*args, **kwargs)

    推荐学习网站:http://devdocs.io/

     

    二  通用视图

    通用视图(generic class base view) 和 class base view 概念上不是一回事

    Class Base View 是指用类的方式去写视图

    通用视图 是用Class Base View的方式将我们常用的增、删、改、查封装成可扩展的类,使用时直接继承、快速实现

     

    1.  通用视图 - ListView

    配置编程,获取数据列表

    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()
    
        def __str__(self):
            return self.name
    
    
    class Author(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=40)
        email = models.EmailField()
    
        def __str__(self):
            return '%s %s' % (self.first_name, self.last_name)
    
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        authors = models.ManyToManyField('Author')
        publisher = models.ForeignKey('Publisher')
        publication_date = models.DateField(auto_now_add=True)
    
        def __str__(self):
            return self.title
    models.py
    from django.views.generic import ListView
    from .models import Publisher
    
    class PublisherList(ListView):
        model = Publisher  # 类属性:指定解析的model
        # queryset = Publisher.objects.all()[0:1]      # 和model二选一,获取指定数据
        context_object_name = 'publishers'             # 默认object_list,前端渲染时的上下文参数
        # template_name = 'books/publisher_list.html'  # 指定渲染的模板文件,默认值model名小写_list.html
    
        # 联系类的queryset,而该方法为实例的定制
        def get_queryset(self):
            print(self.request)  # 根据 self.request进行判断,返回符合条件的queryset
            return Publisher.objects.all()[0:1]

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

    说明:

    • multi object list view

    • model、queryset和get_queryset三者之间关系

    • content_object_name

    • template_name

    源码UML图分析:

    2.  通用视图 - DetailView

    显示一个object的详情页面

    from django.views.generic import DetailView
    from .models import Publisher
    from .models import Book
    
    class PublisherDetail(DetailView):
        model = Publisher
        context_object_name = 'publisher'  # 模板渲染对象,默认值object
    
        def get_context_data(self, **kwargs):
            context = super(PublisherDetail, self).get_context_data(**kwargs)
            context['book_list'] = Book.objects.all()  # 附加额外选软数据
            return context
        
    '''
        # 默认的object,是从url里的pk获取的
        def get_object(self, queryset=None):
            object = super(PublisherDetail, self).get_object()
            # 进行更新操作
            # object.last_accessed = timezone.now()
            # object.save()
            return object
    '''

    urlpatterns = [
    url(r'^publisher/(?P<pk>[0-9]+)/$', views.PublisherDetail.as_view(), name='publisher-detail'),
    ]

    说明:

    • Single object detail view

    • get_context_data

    • content_object_name

    • get_object

    源码UML图分析:

    3.  通用视图 - FormView

    • FormView

    • CreateView,UpdateView,DeleteView

  • 相关阅读:
    我的知识库(4) java获取页面编码(Z)
    知识库(3)JAVA 正则表达式 (超详细)
    The Struts dispatcher cannot be found. This is usually caused by using Struts tags without the associated filter. Struts
    某人总结的《英语听力的技巧 》,挺搞的
    我的知识库(5)java单例模式详解
    构建可扩展程序
    SerialPort (RS232 Serial COM Port) in C# .NET
    Python学习笔记——String、Sequences
    UI题目我的答案
    jQuery学习系列学会操纵Form表单元素(1)
  • 原文地址:https://www.cnblogs.com/jonathan1314/p/7545298.html
Copyright © 2011-2022 走看看