Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django class-based views: survival guide for novices

Django class-based views: survival guide for novices

The slides of the presentation I gave at DjangoCon 2014

Are you a Django novice, confused by words like class-based views, URL dispatchers, HTTP requests? Are you still wondering how to use all those things to build the pages of your Web site?

Django programmers that started with versions prior to 1.3 are used to deal with views as functions, and they learned how to process even complex forms in a procedural way. From the release 1.3, Django introduced class-based views (CBVs) and ported its powerful generic views to this new paradigm (class-based generic views, or CBGVs).

This change, however, has not been harmless for Django novices: the django-users mailing list and StackOverflow are full of questions about views and classes.

This talk aims to lead Django novices to a good understanding of what class-based functions are and how they can be effectively used.

The main topics are:

* Python classes: how OOP concepts improve the View part of Django MVT. This part aims to introduce Python classes as data processors and explains how OOP concepts like inheritance help the fast development of customized solutions.

* URL dispatchers: how Django CBV process URL parameters. Here I discuss how Django class-based views store arguments extracted from URLs and how we can access them.

* HTTP verbs: how Django CBV deal with GET, POST and friends. This part shows what happens to a class-based view when HTTP requests are processed and how to leverage the mechanism to customize data processing.

* CRUD operations through Django generic class-based views. Create, Read, Update, Delete are the fundamentals operations you need on data, so it is worth learning to use and customize the powerful generic views of Django that implement them.

The target of this talk are Django novices who completed and understood the Django tutorial. Previous knowledge of the basic Python OOP syntax and concepts is preferred (classes, inheritance, method overriding, function arguments processing).

http://lgiordani.com
https://twitter.com/tw_lgiordani/

Leonardo Giordani

May 13, 2014
Tweet

More Decks by Leonardo Giordani

Other Decks in Programming

Transcript

  1. Django class-based views - Survival guide for novices DjangoCon 2014

    #2/55 A try to help you understand WHAT class-based views are and HOW they work so that you may solve your problems.
  2. Django class-based views - Survival guide for novices DjangoCon 2014

    #3/55 Target of this talk Django novices who completed and understood the Django tutorial. Knowledge of the basic Python OOP syntax and concepts is useful (classes, inheritance, method overriding, function arguments processing). About you
  3. Django class-based views - Survival guide for novices DjangoCon 2014

    #4/55 Start from the basics What are Django class-based views?
  4. Django class-based views - Survival guide for novices DjangoCon 2014

    #5/55 Start from the basics (Django) Views based on (Python) classes.
  5. Django class-based views - Survival guide for novices DjangoCon 2014

    #6/55 Start from the basics (Django) Views based on (Python) classes. (Django) Stuff with some (Python) magic.
  6. Django class-based views - Survival guide for novices DjangoCon 2014

    #8/55 Django is a processor of HTTP requests. Django HTTP request HTTP response Views
  7. Django class-based views - Survival guide for novices DjangoCon 2014

    #9/55 A view is the part of Django that processes a specific request (HTTP method on URL). Views (GET) URL1 Django view1 view2 (POST) URL2 RESPONSE1 RESPONSE2
  8. Django class-based views - Survival guide for novices DjangoCon 2014

    #10/55 Views HTTP REQUEST view HTTP RESPONSE HTTP REQUEST enhanced view HTTP RESPONSE A view can be monolithic. This makes hard to replace or enhance part of it.
  9. Django class-based views - Survival guide for novices DjangoCon 2014

    #11/55 Views A system can be splitted in several components. This makes easier to change part of it. HTTP REQUEST view:step1 HTTP RESPONSE view:step2 view:step3 HTTP REQUEST view:step1 HTTP RESPONSE view:step2 view:step3
  10. Django class-based views - Survival guide for novices DjangoCon 2014

    #12/55 Object-oriented paradigm Object-oriented paradigm A way to build componentized systems that may be easily changed.
  11. Django class-based views - Survival guide for novices DjangoCon 2014

    #13/55 Class-based views from django.views.generic.list import ListView from articles.models import Article from django.conf.urls import url class ArticleListView(ListView): model = Article urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()), ]
  12. Django class-based views - Survival guide for novices DjangoCon 2014

    #14/55 Class-based views This routes HTTP requests on the 'articles/' URL to the ArticleListView view. from django.views.generic.list import ListView from articles.models import Article from django.conf.urls import url class ArticleListView(ListView): model = Article urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()), ]
  13. Django class-based views - Survival guide for novices DjangoCon 2014

    #15/55 Class-based views This defines the view as a copy of ListView working on the Article model. from django.views.generic.list import ListView from articles.models import Article from django.conf.urls import url class ArticleListView(ListView): model = Article urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()), ]
  14. Django class-based views - Survival guide for novices DjangoCon 2014

    #16/55 Class-based views • Processes incoming HTTP GET requests • Loads all Article objects • Renders a template called article_list.html and the list of articles is in the object_list variable class ArticleListView(ListView): model = Article
  15. Django class-based views - Survival guide for novices DjangoCon 2014

    #17/55 Class-based views What happens behind the scenes?
  16. Django class-based views - Survival guide for novices DjangoCon 2014

    #18/55 Advertising Use the source, Luke! https://github.com/django/django https://github.com/django/django
  17. Django class-based views - Survival guide for novices DjangoCon 2014

    #19/55 Warning Source is a moving target https://github.com/django/django/tree/1.5.7
  18. Django class-based views - Survival guide for novices DjangoCon 2014

    #20/55 A tour of a CBV GET request as_view() url(r'^articles/$', ArticleListView.as_view())
  19. Django class-based views - Survival guide for novices DjangoCon 2014

    #21/55 GET request as_view() url(r'^articles/$', ArticleListView.as_view()) @classonlymethod def as_view(cls, **initkwargs): [...] def view(request, *args, **kwargs): [...] self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) [...] return view django/views/generic/base.py#L46 A tour of a CBV
  20. Django class-based views - Survival guide for novices DjangoCon 2014

    #22/55 GET request dispatch() as_view() return self.dispatch(request, *args, **kwargs) 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) django/views/generic/base.py#L78 A tour of a CBV
  21. Django class-based views - Survival guide for novices DjangoCon 2014

    #23/55 GET request dispatch() as_view() return self.dispatch(request, *args, **kwargs) 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) django/views/generic/base.py#L78 A tour of a CBV 'GET' --> getattr(self, 'get', [...]) 'POST' --> getattr(self, 'post', [...]) 'PUT' --> getattr(self, 'put', [...]) [...] request.method.lower()
  22. Django class-based views - Survival guide for novices DjangoCon 2014

    #24/55 dispatch() GET request get() as_view() return self.get(request, *args, **kwargs) A tour of a CBV
  23. Django class-based views - Survival guide for novices DjangoCon 2014

    #25/55 A tour of a CBV dispatch() GET request get() as_view() return self.get(request, *args, **kwargs) def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty: if (self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists')): is_empty = not self.object_list.exists() else: is_empty = len(self.object_list) == 0 if is_empty: raise Http404 [...] context = self.get_context_data(object_list=self.object_list) return self.render_to_response(context) django/views/generic/list.py#L123 class ArticleListView(ListView):
  24. Django class-based views - Survival guide for novices DjangoCon 2014

    #26/55 A tour of a CBV dispatch() GET request get() as_view() return self.get(request, *args, **kwargs) def get(self, request, *args, **kwargs): self.object_list = self.get_queryset() allow_empty = self.get_allow_empty() if not allow_empty: if (self.get_paginate_by(self.object_list) is not None and hasattr(self.object_list, 'exists')): is_empty = not self.object_list.exists() else: is_empty = len(self.object_list) == 0 if is_empty: raise Http404 [...] context = self.get_context_data(object_list=self.object_list) return self.render_to_response(context) self.object_list = self.get_queryset() context = self.get_context_data( object_list=self.object_list) django/views/generic/list.py#L123 return self.render_to_response(context)
  25. Django class-based views - Survival guide for novices DjangoCon 2014

    #27/55 dispatch() GET request get() as_view() get_queryset() self.object_list = self.get_queryset() A tour of a CBV def get_queryset(self): if self.queryset is not None: queryset = self.queryset if hasattr(queryset, '_clone'): queryset = queryset._clone() elif self.model is not None: queryset = self.model._default_manager.all() else: raise ImproperlyConfigured [...] return queryset django/views/generic/list.py#L22
  26. Django class-based views - Survival guide for novices DjangoCon 2014

    #28/55 dispatch() GET request get() as_view() get_queryset() self.object_list = self.get_queryset() A tour of a CBV def get_queryset(self): if self.queryset is not None: queryset = self.queryset if hasattr(queryset, '_clone'): queryset = queryset._clone() elif self.model is not None: queryset = self.model._default_manager.all() else: raise ImproperlyConfigured [...] return queryset django/views/generic/list.py#L22 queryset = self.model._default_manager.all() class ArticleListView(ListView): model = Article
  27. Django class-based views - Survival guide for novices DjangoCon 2014

    #29/55 Override methods How do you customize CBVs behaviour?
  28. Django class-based views - Survival guide for novices DjangoCon 2014

    #30/55 GET request as_view() Override methods class ArticleListView(ListView): model = Article
  29. Django class-based views - Survival guide for novices DjangoCon 2014

    #31/55 Override methods class ArticleListView(ListView): model = Article def get_queryset(self): queryset = super(ArticleListView, self).get_queryset() return queryset.filter([...]) dispatch() GET request get() as_view() get_queryset() def get_queryset(self): [...] views/generic/list.py#L22
  30. Django class-based views - Survival guide for novices DjangoCon 2014

    #33/55 @classonlymethod def as_view(cls, **initkwargs): [...] def view(request, *args, **kwargs): [...] self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) [...] return view View parameters and request are stored in the view object. Arguments in CBVs
  31. Django class-based views - Survival guide for novices DjangoCon 2014

    #34/55 class ArticleListView(ListView): model = Article def get_queryset(self): queryset = super(ArticleListView, self).get_queryset() return queryset.filter(year=kwargs['year']) https://docs.djangoproject.com/en/1.5/topics/http/urls/ url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view()), Arguments in CBVs
  32. Django class-based views - Survival guide for novices DjangoCon 2014

    #36/55 CRUD operations CRUD through HTTP verbs Read: GET Create: POST Update: PUT Delete: DELETE
  33. Django class-based views - Survival guide for novices DjangoCon 2014

    #37/55 GET request dispatch() as_view() return self.dispatch(request, *args, **kwargs) 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) django/views/generic/base.py#L78 'GET' --> getattr(self, 'get', [...]) 'POST' --> getattr(self, 'post', [...]) 'PUT' --> getattr(self, 'put', [...]) [...] request.method.lower() CRUD operations
  34. Django class-based views - Survival guide for novices DjangoCon 2014

    #38/55 POST request as_view() class NoteAdd(CreateView): model = StickyNote CRUD operations
  35. Django class-based views - Survival guide for novices DjangoCon 2014

    #39/55 dispatch() POST request post() as_view() return self.post(request, *args, **kwargs) class ProcessFormView(View): def post(self, request, *args, **kwargs): form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) CRUD operations django/views/generic/edit.py#L157
  36. Django class-based views - Survival guide for novices DjangoCon 2014

    #40/55 dispatch() POST request post() as_view() return self.post(request, *args, **kwargs) class ProcessFormView(View): def post(self, request, *args, **kwargs): form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) CRUD operations django/views/generic/edit.py#L157 form_class = self.get_form_class() form = self.get_form(form_class) return self.form_valid(form) return self.form_invalid(form)
  37. Django class-based views - Survival guide for novices DjangoCon 2014

    #41/55 class FormMixin(ContextMixin): def get_form_class(self): return self.form_class def get_form(self, form_class): return form_class(**self.get_form_kwargs()) def get_form_kwargs(self): kwargs = {'initial': self.get_initial()} if self.request.method in ('POST', 'PUT'): kwargs.update({'data': self.request.POST,'files': self.request.FILES,}) return kwargs def form_valid(self, form): return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, form): return self.render_to_response(self.get_context_data(form=form)) CRUD operations django/views/generic/edit.py#L10
  38. Django class-based views - Survival guide for novices DjangoCon 2014

    #43/55 CRUD operations browser server GET request The user browses a web page (GET)
  39. Django class-based views - Survival guide for novices DjangoCon 2014

    #44/55 CRUD operations browser server GET request browser server HTTP response The server answers the GET request with a page containing a form
  40. Django class-based views - Survival guide for novices DjangoCon 2014

    #45/55 CRUD operations browser server GET request browser server HTTP response browser server POST request The user fills the form and submits it (POST)
  41. Django class-based views - Survival guide for novices DjangoCon 2014

    #46/55 CRUD operations browser server GET request browser server HTTP response browser server POST request browser server HTTP response The server processes POST data and return a response
  42. Django class-based views - Survival guide for novices DjangoCon 2014

    #47/55 Double interaction, the view is called twice CRUD operations
  43. Django class-based views - Survival guide for novices DjangoCon 2014

    #48/55 from django.shortcuts import render from django.http import HttpResponseRedirect def contact(request): if request.method == 'POST': # If the form has been submitted... form = ContactForm(request.POST) # A form bound to the POST data if form.is_valid(): # All validation rules pass # Process the data in form.cleaned_data # ... return HttpResponseRedirect('/thanks/') # Redirect after POST else: form = ContactForm() # An unbound form return render(request, 'contact.html', {'form': form,}) CRUD operations
  44. Django class-based views - Survival guide for novices DjangoCon 2014

    #49/55 GET request dispatch() as_view() return self.dispatch(request, *args, **kwargs) 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) django/views/generic/base.py#L78 handler = self.http_method_not_allowed CRUD operations
  45. Django class-based views - Survival guide for novices DjangoCon 2014

    #50/55 Managing HTTP methods class RedirectView(View): def get(self, request, *args, **kwargs): [...] return http.HttpResponseRedirect(url) def head(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def options(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) django/views/generic/base.py#L157
  46. Django class-based views - Survival guide for novices DjangoCon 2014

    #51/55 Django CBVs class hierarchy CBVs browser Resources http://ccbv.co.uk/
  47. Django class-based views - Survival guide for novices DjangoCon 2014

    #52/55 Django CBVs class hierarchy Django Vanilla Views Resources http://django-vanilla-views.org/
  48. Django class-based views - Survival guide for novices DjangoCon 2014

    #53/55 Where this talk comes from Digging Up Django Class-based Views http://lgiordani.github.io Resources
  49. Django class-based views - Survival guide for novices DjangoCon 2014

    #54/55 Some links to better understand Python OOP Google: “Some links to better understand Python OOP” Resources http://redd.it/226ahl
  50. Django class-based views - Survival guide for novices DjangoCon 2014

    #55/55 About me Leonardo Giordani http://lgiordani.github.io https://twitter.com/tw_lgiordani https://github.com/lgiordani https://plus.google.com/u/LeonardoGiordani Feel free to contact me Questions, suggestions, corrections are always warmly welcome