本节内容

一   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

相关文章: