intro.txt 12 KB

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