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

Class Based Views - Past, Present and Future

Class Based Views - Past, Present and Future

One of the big changes in Django 1.3 was the introduction of Class-Based Views. Opinion on them is strongly divided; some love them, some hate them.

In this talk, we'll look at the history that led to the development of Class-Based Views, and the current state of play. We'll look at some interesting design patterns that are possible by exploiting the features of Class-Based views. And we'll look to what the future could hold for Django's Class-based future.

Russell Keith-Magee

August 01, 2014
Tweet

More Decks by Russell Keith-Magee

Other Decks in Programming

Transcript

  1. In the beginning... • It’s 2005 • Django is for

    building websites • Views are for displaying content • There a lots of common patterns in views • We all like being DRY
  2. Generic Views • Display a template • Display a single

    object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive
  3. Example: Before def edit_book(request, book_id):! try:! book = Book.objects.get(pk=book_id)! except

    Book.DoesNotExist:! raise Http404! if request.method == 'POST':! form = BookForm(! instance=book, ! data=request.POST)! if form.is_valid():! form.save()! return HttpResponseRedirect(! book.get_absolute_url())! ...
  4. Problems • Configuration options limited by args • No control

    over logic flow • No re-use between views
  5. Example: Before def edit_book(request, book_id):! try:! book = Book.objects.get(pk=book_id)! except

    Book.DoesNotExist:! raise Http404! if request.method == 'POST':! form = BookForm(! instance=book, ! data=request.POST)! if form.is_valid():! ! form.save()! return HttpResponseRedirect(! book.get_absolute_url())! ...
  6. Example: Before def edit_book(request, book_id):! try:! book = Book.objects.get(pk=book_id)! except

    Book.DoesNotExist:! raise Http404! if request.method == 'POST':! form = BookForm(! instance=book, ! data=request.POST)! if form.is_valid():! book.prepare()! form.save()! return HttpResponseRedirect(! book.get_absolute_url())! ...
  7. What went wrong? • Fundamental confusion over purpose • Confusion

    over implementation choices • Ravioli Code • Bad documentation
  8. Purpose • Two distinct, but connected bodies of work •

    Class Based Views • Class Based Generic Views
  9. Class Based Views • A class based analog of a

    view function • Method-based HTTP-verb dispatch • ... and that’s it
  10. Example from django.views.generic import View! ! ! class MyView(View):! def

    get(self, request, *args, **kwargs):! return render(request, 'myview.html' {})! ! def post(self, request, *args, **kwargs):! ...!
  11. Class Based Views • Automatic OPTIONS request handling • Automatic

    naïve HEAD request handling • Automatic HTTP 405 on unknown verbs
  12. CB Generic Views • Uses Class Based View as a

    base • Creates analogs of the old generic views • Addresses limits of functional approach
  13. Ravioli • Goal: Replace function-based generics with class-based generics •

    Show what class-based approach can do • End point views built out of mixins
  14. Mixins def edit_view(): get object if POST: get form if

    form is valid: handle form redirect else: handle failure else: get form return response
  15. Mixins • UpdateView • BaseUpdateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate
 ResponseMixin • TemplateResponseMixin def edit_view(): get object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response
  16. Mixins • UpdateView • BaseUpdateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate
 ResponseMixin • TemplateResponseMixin def edit_view(): get object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response
  17. Mixins • UpdateView • BaseUpdateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate
 ResponseMixin • TemplateResponseMixin def edit_view(): get object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response
  18. Mixins • UpdateView • BaseUpdateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate
 ResponseMixin • TemplateResponseMixin def edit_view(): get object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response
  19. Mixins • CreateView • BaseCreateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate
 ResponseMixin • TemplateResponseMixin def create_view(): instantiate object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response
  20. Mixins • CreateView • BaseCreateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectJSON
 ResponseMixin def create_view(): instantiate object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return JSON response
  21. Example: After from django.views import generic! ! ! ! !

    urlpatterns = patterns('',! url(! r'^/book/(?P<object_id>\d+)/$',! generic.update_object, {'model': Book,},! name='edit-book'! )! ...!
  22. Example: After CBGV from django.views import generic! ! ! !

    ! urlpatterns = patterns('',! url(! r'^/book/(?P<object_id>\d+)/$',! generic.UpdateView.as_view(model=Book),! name='edit-book'! )! ...!
  23. Example: After CBGV from django.views import generic! ! class BookUpdateView(generic.UpdateView):!

    model = Book! ! urlpatterns = patterns('',! url(! r'^/book/(?P<object_id>\d+)/$',! BookUpdateView.as_view(),! name='edit-book'! )! ...!
  24. Example: After CBGV class BookUpdateView(generic.UpdateView):! model = Book! ! def

    form_valid(self, form):! self.object.process()! return super(BookUpdateView, ! self).form_valid(form)!
  25. Example: After CBGV from random import random! ! class PermissionsMixin(object):!

    def get_object(self, queryset=None):! if random() > 0.8:! raise Http404('Better luck next time')! else:! return super(PermissionsMixin, ! self).get_object(queryset)! ! class BookUpdateView(PermissionsMixin,! generic.UpdateView):! model = Book!
  26. Ravioli tastes good! • Allows for reuse of core logic

    • Extremely flexible for inserting new logic • Easy to add your own mixins • But: You need to grok all the pieces
  27. Documentation • Bad as originally released • Much better now

    • ccbv.co.uk • Still need framework decisions needed
  28. Generic Views • Display a template • Display a single

    object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive
  29. Modern problems • Multiple forms/formsets per page • Continuous scrolling,

    not pagination • AJAX support (including in-place editing) • PJAX • Multiple “actions” per page • Rich, Javascript-driven UI
  30. API-driven UI • A modern web site is an API

    with a UI • A user interface is: • a way to get meat to invoke an API • a way to visualize API results
  31. API-driven UI in Django • APIs are easy • DRF,

    TastyPie and others. • Still need “views” • Navigation • Composition
  32. django.contrib.admin • Django’s admin is a class based view •

    Implemented using simple __call__() • Doesn’t have HTTP Verb support • Suffers from state problems
  33. Call to action • In discussion: Do you mean CBV

    or CBGV? • Docs can still be improved • #18830 - FormCollection • Experiment with APIs. • Django’s admin is a useful case study