Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Introduction to class-based views


May 14, 2021 Django


Table of contents


Introduction to class-based views

Class-based views provide an alternative way to implement views as Python objects rather than functions. They are not a substitute for feature-based views, but they have some differences and advantages over feature-based views:

  • Code organizations associated with specific HTTP methods (GET, POST, etc.) can be solved by a separate method rather than a conditional branch.
  • Object-oriented techniques such as mixin (multi-inheritance) can be used to break code down into reusable components.

Common view, class-based views, and class-based generic views of relationships and history

Initially only the view function contract, Django passes your function to HttpRequest and expects to pass to HttpResponse. That's what Django does.

It was recognized early on that common accustomeds and patterns were found in view development. A common function-based view was introduced to abstract these patterns and simplify view development in common situations.

The problem with a common function-based view is that while they cover simple situations well, they cannot extend or customize views other than some configuration options, limiting their useful use in many real-world applications.

Class-based generic views are created for the same purpose as function-based generic views to make view development easier. However, the solution is implemented by using mixins to provide a toolkit that makes class-based generic views more scalable and flexible than feature-based counterpart views.

If you've tried a universal function-based view in the past and found that these features are missing, you shouldn't think of class-based generic views as class-based equivalent views, but as a new way to solve the original problem that universal views are designed to solve. Solve.

Django is most flexible in building base classes and mixin toolkits for class-based generic views, so they have many hooks in the form of default method implementations and properties, and you may not pay attention to their cases in the simplest usage. F or example, instead of form_class using class-based properties of the get_form method, the implementation uses a method get_form_class that calls a method that returns the properties of form_class classes in its default implementation. T his gives you several options to specify which form the hook can be called from property to full dynamic. For simplicity, these options seem to add hollow complexity, but without them, they limit more advanced design.

Use class-based comments

In essence, class-based views allow you to use different class instance methods to respond to different HTTP request methods instead of using conditional branch code in a single view function.

Therefore, the code that GET uses to handle HTTP in the view function looks like this:

from django.http import HttpResponse

def my_view(request):
  if request.method == 'GET':
      # <view logic>
      return HttpResponse('result')

In a class-based view, this becomes:

from django.http import HttpResponse
from django.views import View

class MyView(View):
  def get(self, request):
      # <view logic>
      return HttpResponse('result')

Because Django's URL parser wants to send requests and associated parameters to callable functions instead of classes, class-based views have a as_view() class method that returns a function that can be called when a request reaches a URL that matches the associated pattern. T he function creates an instance of the class, calls setup() to initialize its properties, and then calls its dispatch() method. Dispatch looks at the request to determine if it is GET, POST, etc., and forwards the request to the matching method (if defined), otherwise throwing it up httpResponseNotAllowed:

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
  path('about/', MyView.as_view()),
]

It's worth noting that your method returns the same content that you return from a function-based view, that is, some form of HttpResponse. This means that http shortcuts or TemplateResponse objects can be used effectively in class-based views.

Although the smallest class-based view does not require any class properties to perform its work, class properties are useful in many class-based designs and have two ways to configure or set class properties.

The first is the standard Python method for subsystyleing and overwriting properties and methods in subsystyles. This way, if your parent class has such a property greeting:

from django.http import HttpResponse
from django.views import View

class GreetingView(View):
  greeting = "Good Day"

  def get(self, request):
      return HttpResponse(self.greeting)

You can override it in sub-classes:

class MorningGreetingView(GreetingView):
  greeting = "Morning to ya"

Another option is to configure the class property to as_view parameters called in URLconf:

urlpatterns = [
  path('about/', GreetingView.as_view(greeting="G'day")),
]

Note: When instantiated your class for each request assigned to it, the class properties set by the import point through as_view() are configured only once when the URL is imported.

Use blending

Mixins is a form of multiple inheritance that combines the behavior and properties of multiple parent classes.

For example, in a generic class-based view, there is a mixin, templateResponseMixin whose primary purpose is to define the render_to_response (). W hen combined with the behavior of the View base class, the result is a TemplateView class that assigned the request to the appropriate matching method (the behavior defined in the View base class) and has a render_to_response() method that returns the TempleResponse object (behavior) using the template_name property. TemplateResponseMixin is defined in .

Mixins is a great way to reuse code across multiple classes, but they come at some cost. The more times your code is scattered in mixin, the more difficult it is to read sub-classes and understand their exact operations, if you are sub-classes with a deep inheritance tree.

Also note that you can only inherit from one generic view - that is, only one parent class can inherit, and the rest of the View, if any, should be mixins. Attempting to inherit View from multiple inherited classes - for example, trying to use a form at the top of the list and combining ProcessFormView and ListView - will not work as expected.

Use class-based views to work with forms

The basic function-based view of the processing form might look like this:

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import MyForm

def myview(request):
  if request.method == "POST":
      form = MyForm(request.POST)
      if form.is_valid():
          # <process form cleaned data>
          return HttpResponseRedirect('/success/')
  else:
      form = MyForm(initial={'key': 'value'})

  return render(request, 'form_template.html', {'form': form})

A similar class-based view might look like this:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from .forms import MyForm

class MyFormView(View):
  form_class = MyForm
  initial = {'key': 'value'}
  template_name = 'form_template.html'

  def get(self, request, *args, **kwargs):
      form = self.form_class(initial=self.initial)
      return render(request, self.template_name, {'form': form})

  def post(self, request, *args, **kwargs):
      form = self.form_class(request.POST)
      if form.is_valid():
          # <process form cleaned data>
          return HttpResponseRedirect('/success/')

      return render(request, self.template_name, {'form': form})

This is the smallest case, but you can see that you can override one or more methods (or both) by overwriting any class property (for example, form_class, through URLconf configuration, or subsyscation and overwriting one or more methods! to customize this view. .

Decorative class-based views

The extension of class-based views is not limited to using blending. Y ou can also use the decorator. Because class-based views are not functions, they work differently depending on whether you are using as_view() or creating sub-classes to decorate them.

Decorated in URLconf

You can adjust as_view by decorating the results of the () method. The easiest way is to deploy the view in URLconf:

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

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

This method applies the decorator based on each instance. If you want to decorate each instance of the view, you need to take a different approach.

Decorative class

To decorate each instance of a class-based view, you need to decorate the class definition itself. To do this, you can apply the decorator to the method of the dispatch() class.

The method on the class is not exactly the same as the stand-alone function, so you can't just apply the function decorator to the method - you need to convert it to a method decorator first. T he method_decorator to convert the function decoration into a method decoration, so that it can be in an instance method. For example:

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().dispatch(*args, **kwargs)

Or, more succinctly, you can replace the decoration class and pass the name of the method you want to decorate as a keyword parameter:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

If you use a set of universal decorators in multiple places, you can define a list of decorators or groups, and then use it instead of calling method_decorator() multiple times. The two classes are equivalent:

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

The decorator will process the request in the order in which it is passed to the decorator. In the example, never_cache() will handle the request login_required ().

In this example, each instance of ProtectedView will have login protection. These examples use login_required, but by using LoginRequiredMixin, you can get the same behavior.

Note: method_decorator the modified methods in the class, using the methods of s args and skwargs as parameters. If your method does not accept a set of compatible parameters, it throws a TypeError exception.

For more information: https://docs.djangoproject.com/en/3.0/