This page looks best with JavaScript enabled

Django Class-Based Views

 ·  ☕ 2 min read

Django has two kinds of views: function-based views and class-based views. The role of a view is mainly to fill in logic and return a response body. Function-based views are hard to extend and have a low rate of code reuse. Class-based views, on the other hand, can use inheritance and mixins to reuse and extend functionality quickly. This article mainly discusses how Django processes class-based views and how class-based view decorators are implemented.

1. Django’s Views

Django’s URL resolver passes an HttpRequest object and the corresponding arguments to a callable function, and expects it to return an HttpResponse object. That callable function is the view function.

1.1 Function-Based Views

views.py

1
2
3
4
5
6
7
8
9
from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # 填充逻辑
        return HttpResponse('result')
    if request.method == 'POST':
        # 填充逻辑
        return HttpResponse('result')

urls.py

1
2
3
4
5
6
7
# urls.py
from django.conf.urls import patterns
import .views as home_view

urlpatterns = patterns('',
    (r'^my_view/', home_view.my_view)
)

Function-based views (FBV) are only used for custom error handling, or in situations where implementing things with class-based views would be very complex.

1.2 Class-Based Views

views.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django.http import HttpResponse
from django.views.generic.base import View

class MyView(View):
    def get(self, request):
        # 填充逻辑
        return HttpResponse('result')
    def post(self, request):
        # 填充逻辑
        return HttpResponse('result')

urls.py

1
2
3
4
5
6
7
# urls.py
from django.conf.urls import patterns
from .views import MyView

urlpatterns = patterns('',
    (r'^about/', MyView.as_view()),
)

To decouple views from URLs and to reuse code, Django provides class-based views.

A class-based view (CBV) provides an as_view() static method. Calling that method creates an instance of the class. It then calls the instance’s dispatch() method, and dispatch() calls the like-named method on the instance according to the type of the request. If no matching method is found, it raises an HttpResponseNotAllowed exception.

The series of class-based view classes Django provides all inherit from a single base class, View (django.views.generic.base.View). This base class implements the interface to URLs (as_view), request method matching (dispatch), and some other basic functionality. For example, RedirectView implements HTTP redirection, and TemplateView adds a method for rendering templates.

The as_view and dispatch methods of the View class in django.views.generic.base.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@classonlymethod
def as_view(cls, **initkwargs):
    for key in initkwargs:
        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__))
        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)
        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)

    update_wrapper(view, cls, updated=())

    update_wrapper(view, cls.dispatch, assigned=())
    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)

2. Django View Mixin Classes

Django abstracts and encapsulates the basic HTTP request and response into classes. In use, you only need to aggregate these base classes and override or directly reuse them in whatever way your requirements demand. These base classes are called mixins.

In the django.views.generic package, besides base, which provides the few most fundamental mixins for building a CBV plus the View base class for CBVs, there are four more modules,

  • detail, for displaying detailed data: SingleObjectMixin, SingleObjectTemplateResponseMixin
  • list, for displaying lists: MultipleObjectMixin, MultipleObjectTemplateResponseMixin
  • edit, providing create and edit functionality: DeletionMixin, FormMixin
  • dates, for displaying and retrieving year/month/day related data: YearMixin, MonthMixin, DayMixin, WeekMixin, DateMixin

A view class can inherit from multiple mixins, but can only inherit from one View (including its subclasses).

3. Class-Based View Decorators

3.1 Decorating dispatch

The dispatch decorator affects all method functions of the view class. Starting with Django 1.9, method_decorator supports a name argument, which lets you specify a method, for example name=‘get’ to decorate only the get function. If multiple decorators need to be configured, @method_decorator also accepts a list argument, so several decorators can be assembled at once.

1
2
3
4
5
6
7
8
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic.base import View

class MyView(View):
    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(MyView, self).dispatch(*args, **kwargs)

Usage of a decorator with arguments:
@method_decorator(login_required_by_role(‘super’))

3.2 Decorating the View Class

Decorating the class, like decorating dispatch, also affects all HTTP methods.

1
2
3
4
5
6
7
8
9
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic.base import View

@method_decorator(login_required)
class MyView(View):

    def dispatch(self, *args, **kwargs):
        return super(MyView, self).dispatch(*args, **kwargs)

3.3 Configuring Decorators in URLConf

1
2
3
4
5
6
7
8
9
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))),
    url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())),
]

4. References


微信公众号
WRITTEN BY
微信公众号