1. =================================
    
  2. Introduction to class-based views
    
  3. =================================
    
  4. 
    
  5. Class-based views provide an alternative way to implement views as Python
    
  6. objects instead of functions. They do not replace function-based views, but
    
  7. have certain differences and advantages when compared to function-based views:
    
  8. 
    
  9. * Organization of code related to specific HTTP methods (``GET``, ``POST``,
    
  10.   etc.) can be addressed by separate methods instead of conditional branching.
    
  11. 
    
  12. * Object oriented techniques such as mixins (multiple inheritance) can be
    
  13.   used to factor code into reusable components.
    
  14. 
    
  15. The relationship and history of generic views, class-based views, and class-based generic views
    
  16. ===============================================================================================
    
  17. 
    
  18. In the beginning there was only the view function contract, Django passed your
    
  19. function an :class:`~django.http.HttpRequest` and expected back an
    
  20. :class:`~django.http.HttpResponse`. This was the extent of what Django provided.
    
  21. 
    
  22. Early on it was recognized that there were common idioms and patterns found in
    
  23. view development. Function-based generic views were introduced to abstract
    
  24. these patterns and ease view development for the common cases.
    
  25. 
    
  26. The problem with function-based generic views is that while they covered the
    
  27. simple cases well, there was no way to extend or customize them beyond some
    
  28. configuration options, limiting their usefulness in many real-world
    
  29. applications.
    
  30. 
    
  31. Class-based generic views were created with the same objective as
    
  32. function-based generic views, to make view development easier. However, the way
    
  33. the solution is implemented, through the use of mixins, provides a toolkit that
    
  34. results in class-based generic views being more extensible and flexible than
    
  35. their function-based counterparts.
    
  36. 
    
  37. If you have tried function based generic views in the past and found them
    
  38. lacking, you should not think of class-based generic views as a class-based
    
  39. equivalent, but rather as a fresh approach to solving the original problems
    
  40. that generic views were meant to solve.
    
  41. 
    
  42. The toolkit of base classes and mixins that Django uses to build class-based
    
  43. generic views are built for maximum flexibility, and as such have many hooks in
    
  44. the form of default method implementations and attributes that you are unlikely
    
  45. to be concerned with in the simplest use cases. For example, instead of
    
  46. limiting you to a class-based attribute for ``form_class``, the implementation
    
  47. uses a ``get_form`` method, which calls a ``get_form_class`` method, which in
    
  48. its default implementation returns the ``form_class`` attribute of the class.
    
  49. This gives you several options for specifying what form to use, from an
    
  50. attribute, to a fully dynamic, callable hook. These options seem to add hollow
    
  51. complexity for simple situations, but without them, more advanced designs would
    
  52. be limited.
    
  53. 
    
  54. Using class-based views
    
  55. =======================
    
  56. 
    
  57. At its core, a class-based view allows you to respond to different HTTP request
    
  58. methods with different class instance methods, instead of with conditionally
    
  59. branching code inside a single view function.
    
  60. 
    
  61. So where the code to handle HTTP ``GET`` in a view function would look
    
  62. something like::
    
  63. 
    
  64.     from django.http import HttpResponse
    
  65. 
    
  66.     def my_view(request):
    
  67.         if request.method == 'GET':
    
  68.             # <view logic>
    
  69.             return HttpResponse('result')
    
  70. 
    
  71. In a class-based view, this would become::
    
  72. 
    
  73.     from django.http import HttpResponse
    
  74.     from django.views import View
    
  75. 
    
  76.     class MyView(View):
    
  77.         def get(self, request):
    
  78.             # <view logic>
    
  79.             return HttpResponse('result')
    
  80. 
    
  81. Because Django's URL resolver expects to send the request and associated
    
  82. arguments to a callable function, not a class, class-based views have an
    
  83. :meth:`~django.views.generic.base.View.as_view` class method which returns a
    
  84. function that can be called when a request arrives for a URL matching the
    
  85. associated pattern. The function creates an instance of the class, calls
    
  86. :meth:`~django.views.generic.base.View.setup` to initialize its attributes, and
    
  87. then calls its :meth:`~django.views.generic.base.View.dispatch` method.
    
  88. ``dispatch`` looks at the request to determine whether it is a ``GET``,
    
  89. ``POST``, etc, and relays the request to a matching method if one is defined,
    
  90. or raises :class:`~django.http.HttpResponseNotAllowed` if not::
    
  91. 
    
  92.     # urls.py
    
  93.     from django.urls import path
    
  94.     from myapp.views import MyView
    
  95. 
    
  96.     urlpatterns = [
    
  97.         path('about/', MyView.as_view()),
    
  98.     ]
    
  99. 
    
  100. 
    
  101. It is worth noting that what your method returns is identical to what you
    
  102. return from a function-based view, namely some form of
    
  103. :class:`~django.http.HttpResponse`. This means that
    
  104. :doc:`http shortcuts </topics/http/shortcuts>` or
    
  105. :class:`~django.template.response.TemplateResponse` objects are valid to use
    
  106. inside a class-based view.
    
  107. 
    
  108. While a minimal class-based view does not require any class attributes to
    
  109. perform its job, class attributes are useful in many class-based designs,
    
  110. and there are two ways to configure or set class attributes.
    
  111. 
    
  112. The first is the standard Python way of subclassing and overriding attributes
    
  113. and methods in the subclass. So that if your parent class had an attribute
    
  114. ``greeting`` like this::
    
  115. 
    
  116.     from django.http import HttpResponse
    
  117.     from django.views import View
    
  118. 
    
  119.     class GreetingView(View):
    
  120.         greeting = "Good Day"
    
  121. 
    
  122.         def get(self, request):
    
  123.             return HttpResponse(self.greeting)
    
  124. 
    
  125. You can override that in a subclass::
    
  126. 
    
  127.     class MorningGreetingView(GreetingView):
    
  128.         greeting = "Morning to ya"
    
  129. 
    
  130. Another option is to configure class attributes as keyword arguments to the
    
  131. :meth:`~django.views.generic.base.View.as_view` call in the URLconf::
    
  132. 
    
  133.     urlpatterns = [
    
  134.         path('about/', GreetingView.as_view(greeting="G'day")),
    
  135.     ]
    
  136. 
    
  137. .. note::
    
  138. 
    
  139.     While your class is instantiated for each request dispatched to it, class
    
  140.     attributes set through the
    
  141.     :meth:`~django.views.generic.base.View.as_view` entry point are
    
  142.     configured only once at the time your URLs are imported.
    
  143. 
    
  144. Using mixins
    
  145. ============
    
  146. 
    
  147. Mixins are a form of multiple inheritance where behaviors and attributes of
    
  148. multiple parent classes can be combined.
    
  149. 
    
  150. For example, in the generic class-based views there is a mixin called
    
  151. :class:`~django.views.generic.base.TemplateResponseMixin` whose primary purpose
    
  152. is to define the method
    
  153. :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`.
    
  154. When combined with the behavior of the :class:`~django.views.generic.base.View`
    
  155. base class, the result is a :class:`~django.views.generic.base.TemplateView`
    
  156. class that will dispatch requests to the appropriate matching methods (a
    
  157. behavior defined in the ``View`` base class), and that has a
    
  158. :meth:`~django.views.generic.base.TemplateResponseMixin.render_to_response`
    
  159. method that uses a
    
  160. :attr:`~django.views.generic.base.TemplateResponseMixin.template_name`
    
  161. attribute to return a :class:`~django.template.response.TemplateResponse`
    
  162. object (a behavior defined in the ``TemplateResponseMixin``).
    
  163. 
    
  164. Mixins are an excellent way of reusing code across multiple classes, but they
    
  165. come with some cost. The more your code is scattered among mixins, the harder
    
  166. it will be to read a child class and know what exactly it is doing, and the
    
  167. harder it will be to know which methods from which mixins to override if you
    
  168. are subclassing something that has a deep inheritance tree.
    
  169. 
    
  170. Note also that you can only inherit from one generic view - that is, only one
    
  171. parent class may inherit from :class:`~django.views.generic.base.View` and
    
  172. the rest (if any) should be mixins. Trying to inherit from more than one class
    
  173. that inherits from ``View`` - for example, trying to use a form at the top of a
    
  174. list and combining :class:`~django.views.generic.edit.ProcessFormView` and
    
  175. :class:`~django.views.generic.list.ListView` - won't work as expected.
    
  176. 
    
  177. 
    
  178. Handling forms with class-based views
    
  179. =====================================
    
  180. 
    
  181. A basic function-based view that handles forms may look something like this::
    
  182. 
    
  183.     from django.http import HttpResponseRedirect
    
  184.     from django.shortcuts import render
    
  185. 
    
  186.     from .forms import MyForm
    
  187. 
    
  188.     def myview(request):
    
  189.         if request.method == "POST":
    
  190.             form = MyForm(request.POST)
    
  191.             if form.is_valid():
    
  192.                 # <process form cleaned data>
    
  193.                 return HttpResponseRedirect('/success/')
    
  194.         else:
    
  195.             form = MyForm(initial={'key': 'value'})
    
  196. 
    
  197.         return render(request, 'form_template.html', {'form': form})
    
  198. 
    
  199. A similar class-based view might look like::
    
  200. 
    
  201.     from django.http import HttpResponseRedirect
    
  202.     from django.shortcuts import render
    
  203.     from django.views import View
    
  204. 
    
  205.     from .forms import MyForm
    
  206. 
    
  207.     class MyFormView(View):
    
  208.         form_class = MyForm
    
  209.         initial = {'key': 'value'}
    
  210.         template_name = 'form_template.html'
    
  211. 
    
  212.         def get(self, request, *args, **kwargs):
    
  213.             form = self.form_class(initial=self.initial)
    
  214.             return render(request, self.template_name, {'form': form})
    
  215. 
    
  216.         def post(self, request, *args, **kwargs):
    
  217.             form = self.form_class(request.POST)
    
  218.             if form.is_valid():
    
  219.                 # <process form cleaned data>
    
  220.                 return HttpResponseRedirect('/success/')
    
  221. 
    
  222.             return render(request, self.template_name, {'form': form})
    
  223. 
    
  224. This is a minimal case, but you can see that you would then have the option
    
  225. of customizing this view by overriding any of the class attributes, e.g.
    
  226. ``form_class``, via URLconf configuration, or subclassing and overriding one or
    
  227. more of the methods (or both!).
    
  228. 
    
  229. Decorating class-based views
    
  230. ============================
    
  231. 
    
  232. The extension of class-based views isn't limited to using mixins. You can also
    
  233. use decorators. Since class-based views aren't functions, decorating them works
    
  234. differently depending on if you're using ``as_view()`` or creating a subclass.
    
  235. 
    
  236. Decorating in URLconf
    
  237. ---------------------
    
  238. 
    
  239. You can adjust class-based views by decorating the result of the
    
  240. :meth:`~django.views.generic.base.View.as_view` method. The easiest place to do
    
  241. this is in the URLconf where you deploy your view::
    
  242. 
    
  243.     from django.contrib.auth.decorators import login_required, permission_required
    
  244.     from django.views.generic import TemplateView
    
  245. 
    
  246.     from .views import VoteView
    
  247. 
    
  248.     urlpatterns = [
    
  249.         path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    
  250.         path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
    
  251.     ]
    
  252. 
    
  253. This approach applies the decorator on a per-instance basis. If you
    
  254. want every instance of a view to be decorated, you need to take a
    
  255. different approach.
    
  256. 
    
  257. .. _decorating-class-based-views:
    
  258. 
    
  259. Decorating the class
    
  260. --------------------
    
  261. 
    
  262. To decorate every instance of a class-based view, you need to decorate
    
  263. the class definition itself. To do this you apply the decorator to the
    
  264. :meth:`~django.views.generic.base.View.dispatch` method of the class.
    
  265. 
    
  266. A method on a class isn't quite the same as a standalone function, so you can't
    
  267. just apply a function decorator to the method -- you need to transform it into
    
  268. a method decorator first. The ``method_decorator`` decorator transforms a
    
  269. function decorator into a method decorator so that it can be used on an
    
  270. instance method. For example::
    
  271. 
    
  272.     from django.contrib.auth.decorators import login_required
    
  273.     from django.utils.decorators import method_decorator
    
  274.     from django.views.generic import TemplateView
    
  275. 
    
  276.     class ProtectedView(TemplateView):
    
  277.         template_name = 'secret.html'
    
  278. 
    
  279.         @method_decorator(login_required)
    
  280.         def dispatch(self, *args, **kwargs):
    
  281.             return super().dispatch(*args, **kwargs)
    
  282. 
    
  283. Or, more succinctly, you can decorate the class instead and pass the name
    
  284. of the method to be decorated as the keyword argument ``name``::
    
  285. 
    
  286.     @method_decorator(login_required, name='dispatch')
    
  287.     class ProtectedView(TemplateView):
    
  288.         template_name = 'secret.html'
    
  289. 
    
  290. If you have a set of common decorators used in several places, you can define
    
  291. a list or tuple of decorators and use this instead of invoking
    
  292. ``method_decorator()`` multiple times. These two classes are equivalent::
    
  293. 
    
  294.     decorators = [never_cache, login_required]
    
  295. 
    
  296.     @method_decorator(decorators, name='dispatch')
    
  297.     class ProtectedView(TemplateView):
    
  298.         template_name = 'secret.html'
    
  299. 
    
  300.     @method_decorator(never_cache, name='dispatch')
    
  301.     @method_decorator(login_required, name='dispatch')
    
  302.     class ProtectedView(TemplateView):
    
  303.         template_name = 'secret.html'
    
  304. 
    
  305. The decorators will process a request in the order they are passed to the
    
  306. decorator. In the example, ``never_cache()`` will process the request before
    
  307. ``login_required()``.
    
  308. 
    
  309. In this example, every instance of ``ProtectedView`` will have login
    
  310. protection. These examples use ``login_required``, however, the same behavior
    
  311. can be obtained by using
    
  312. :class:`~django.contrib.auth.mixins.LoginRequiredMixin`.
    
  313. 
    
  314. .. note::
    
  315. 
    
  316.     ``method_decorator`` passes ``*args`` and ``**kwargs``
    
  317.     as parameters to the decorated method on the class. If your method
    
  318.     does not accept a compatible set of parameters it will raise a
    
  319.     ``TypeError`` exception.