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.

B91373320dbc3bc52fcd870d3b21748f?s=128

Russell Keith-Magee

August 01, 2014
Tweet

Transcript

  1. Class Based Views: Past, Present and Future Dr Russell Keith-Magee

    DjangoCon AU 2014
  2. None
  3. None
  4. Class Based Views

  5. In the beginning...

  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
  7. None
  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
  9. Key features • Each view is a function • Configuration

    via arguments
  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())! ...
  11. Example: Before ...! else:! form = BookForm(instance=obj)! return render(request, 'edit_book.html',

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

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

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

    name='edit-author'! )! ...!
  15. Problems • Configuration options limited by args • No control

    over logic flow • No re-use between views
  16. 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())! ...
  17. 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())! ...
  18. None
  19. Let’s go class-based!

  20. Django 1.3

  21. OMFGBBQ!!1!!

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

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

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

    view function • Method-based HTTP-verb dispatch • ... and that’s it
  25. 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):! ...!
  26. Class Based Views • Automatic OPTIONS request handling • Automatic

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

    base • Creates analogs of the old generic views • Addresses limits of functional approach
  28. Implementation choices https://code.djangoproject.com/wiki/ClassBasedViews

  29. Ravioli • Goal: Replace function-based generics with class-based generics •

    Show what class-based approach can do • End point views built out of mixins
  30. freakboy3742’s  Ravioli Cooking 101

  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. Example: After from django.views import generic! ! ! ! !

    urlpatterns = patterns('',! url(! r'^/book/(?P<object_id>\d+)/$',! generic.update_object, {'model': Book,},! name='edit-book'! )! ...!
  39. 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'! )! ...!
  40. 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'! )! ...!
  41. Example: After CBGV class BookUpdateView(generic.UpdateView):! model = Book! ! def

    form_valid(self, form):! self.object.process()! return super(BookUpdateView, ! self).form_valid(form)!
  42. 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!
  43. 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
  44. Documentation • Bad as originally released • Much better now

    • ccbv.co.uk • Still need framework decisions needed
  45. Where to from here?

  46. Have we solved the wrong problem?

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

    object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive
  48. Modern web sites  have different problems

  49. 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
  50. 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
  51. API-driven UI in Django • APIs are easy • DRF,

    TastyPie and others. • Still need “views” • Navigation • Composition
  52. All of this requires a strong base framework

  53. Class-Based Views provide that framework

  54. 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
  55. Doesn’t need to be part of Django core

  56. 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
  57. Questions? http://djangoproject.org http://cecinestpasun.com russell@keith-magee.com @freakboy3742