Class Based Views: Untangling the Mess

Class Based Views: Untangling the Mess

Presentation from DjangoCon Europe 2014

One of the big changes in Django 1.3 was the introduction of Class-Based Views. Class-Based Views represent one of the biggest stylistic changes in Django since Magic Removal, and there's been lots of confusion surrounding them.

This talk will be a brief tutorial, providing some background and dispelling some of the confusion around the use of Class-Based Views. It will also look at some interesting possibilities that are made possible by the use of the class-based view framework.

B91373320dbc3bc52fcd870d3b21748f?s=128

Russell Keith-Magee

May 17, 2013
Tweet

Transcript

  1. Class Based Views: Untangling the Mess Dr Russell Keith-Magee DjangoCon

    Europe 2013 Thursday, 23 May 13
  2. Thursday, 23 May 13

  3. Thursday, 23 May 13

  4. Class Based Views Thursday, 23 May 13

  5. In the beginning... Thursday, 23 May 13

  6. 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 Thursday, 23 May 13
  7. Thursday, 23 May 13

  8. Generic Views • Display a template • Display a single

    object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive Thursday, 23 May 13
  9. Key features • Each view is a function • Configuration

    via arguments Thursday, 23 May 13
  10. 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()) ... Thursday, 23 May 13
  11. Example: Before ... else: form = BookForm(instance=obj) return render(request, ‘edit_book.html’,

    { ‘book’: book ‘form’: form }) Thursday, 23 May 13
  12. Example: Before urlpatterns = patterns('', url( r’^/book/(?P<book_id>\d+)/$’, book_view, name=‘edit-book’ )

    ... Thursday, 23 May 13
  13. Example: After urlpatterns = patterns('', url( r’^/book/(?P<object_id>\d+)/$’, generic.update_object, {‘model’: Book,},

    name=‘edit-book’ ) ... Thursday, 23 May 13
  14. Example: After urlpatterns = patterns('', url( r’^/author/(?P<object_id>\d+)/$’, generic.update_object, {‘model’: Author,},

    name=‘edit-author’ ) ... Thursday, 23 May 13
  15. Problems • Configuration options limited by args • No control

    over logic flow • No re-use between views Thursday, 23 May 13
  16. Thursday, 23 May 13

  17. Let’s go class-based! Thursday, 23 May 13

  18. Django 1.3 Thursday, 23 May 13

  19. OMFGBBQ!!1!! Thursday, 23 May 13

  20. What went wrong? • Fundamental confusion over purpose • Confusion

    over implementation choices • Ravioli Code • Bad documentation Thursday, 23 May 13
  21. Purpose • Two distinct, but connected bodies of work •

    Class Based Views • Class Based Generic Views Thursday, 23 May 13
  22. Class Based Views • A class based analog of a

    view function • Method-based HTTP-verb dispatch • ... and that’s it Thursday, 23 May 13
  23. 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): ... Thursday, 23 May 13
  24. Class Based Views • Automatic OPTIONS request handling • Automatic

    naïve HEAD request handling • Automatic HTTP 405 on unknown verbs Thursday, 23 May 13
  25. CB Generic Views • Uses Class Based View as a

    base • Creates analogs of the old generic views • Addresses limits of functional approach Thursday, 23 May 13
  26. Implementation choices https://code.djangoproject.com/wiki/ClassBasedViews Thursday, 23 May 13

  27. Obvious choice • A class that is instantiated as a

    view Thursday, 23 May 13
  28. What do you mean? urlpatterns = pattern(‘’, # Deploy the

    Class? (‘myview’, MyView), # or an Instance of a class (‘myview’, MyView()), # or a classmethod? (‘myview’, MyView.as_view), # or a Factory? (‘myview’, MyView.as_view()), ) Thursday, 23 May 13
  29. Problems • What is being instantiated? • How is it

    being instantiated? • When is this class instantiated? • How do you pass in configuration? • What is the lifespan of an instance? • What is the contract with urls.py? Thursday, 23 May 13
  30. Sidebar: Admin • Django’s admin is a class based view

    • Implemented using simple __call__() • Doesn’t have HTTP Verb support • Suffers from state problems Thursday, 23 May 13
  31. Solution • Factory classmethod • Returns function that invokes .dispatch()

    • initkwargs passed to __init__ • initkwargs must be pre-declared • view request, args and kwargs are stateful urlpatterns = pattern(‘’, (‘myview’, MyView.as_view( template_name=‘foo.html’)), ) Thursday, 23 May 13
  32. Sidebar: Other option • Change the urls.py contract • Currently:

    a callable • Change to: a callable or a class. • Decision: keep the urls.py contract clear Thursday, 23 May 13
  33. Ravioli • Goal: Replace function-based generics with class-based generics •

    Show what class-based approach can do • End point views built out of mixins Thursday, 23 May 13
  34. freakboy3742’s Ravioli Cooking 101 Thursday, 23 May 13

  35. Mixins edit_view: get object if POST: get form if form

    is valid: handle form redirect else: handle failure else: get form return response Thursday, 23 May 13
  36. Mixins • UpdateView • BaseUpdateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView • SingleObjectTemplate ResponseMixin • TemplateResponseMixin edit_view: get object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response Thursday, 23 May 13
  37. Mixins •CreateView •BaseCreateView • ModelFormMixin • FormMixin • SingleObjectMixin •

    ContextMixin • ProcessFormView • SingleObjectTemplate ResponseMixin • TemplateResponseMixin create_view: instantiate object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return response Thursday, 23 May 13
  38. Mixins • CreateView • BaseCreateView • ModelFormMixin • FormMixin •

    SingleObjectMixin • ContextMixin • ProcessFormView •SingleObjectJSON ResponseMixin create_view: instantiate object if POST: get form if form is valid: handle form redirect else: handle failure else: get form return JSON response Thursday, 23 May 13
  39. Ravioli tastes good! • Allows for maximum reuse of core

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

    • Still need framework decisions needed Thursday, 23 May 13
  41. Where to from here? Thursday, 23 May 13

  42. Have we solved the wrong problem? Thursday, 23 May 13

  43. Generic Views • Display a template • Display a single

    object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive Thursday, 23 May 13
  44. Modern web sites have different problems Thursday, 23 May 13

  45. Modern problems • Multiple forms/formsets per page • Conditional forms

    • Continuous scrolling, not pagination • AJAX support (including in-place editing) • PJAX • Multiple “actions” per page Thursday, 23 May 13
  46. 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 Thursday, 23 May 13
  47. Questions? http://djangoproject.org http://cecinestpasun.com russell@keith-magee.com @freakboy3742 Thursday, 23 May 13