Slide 1

Slide 1 text

Class Based Views: Past, Present and Future Dr Russell Keith-Magee DjangoCon AU 2014

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Class Based Views

Slide 5

Slide 5 text

In the beginning...

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Generic Views • Display a template • Display a single object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive

Slide 9

Slide 9 text

Key features • Each view is a function • Configuration via arguments

Slide 10

Slide 10 text

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())! ...

Slide 11

Slide 11 text

Example: Before ...! else:! form = BookForm(instance=obj)! return render(request, 'edit_book.html', {! 'book': book! 'form': form! })!

Slide 12

Slide 12 text

Example: Before urlpatterns = patterns('',! url(! r'^/book/(?P\d+)/$',! book_view,! name='edit-book'! )! ...!

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Problems • Configuration options limited by args • No control over logic flow • No re-use between views

Slide 16

Slide 16 text

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())! ...

Slide 17

Slide 17 text

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())! ...

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

Let’s go class-based!

Slide 20

Slide 20 text

Django 1.3

Slide 21

Slide 21 text

OMFGBBQ!!1!!

Slide 22

Slide 22 text

What went wrong? • Fundamental confusion over purpose • Confusion over implementation choices • Ravioli Code • Bad documentation

Slide 23

Slide 23 text

Purpose • Two distinct, but connected bodies of work • Class Based Views • Class Based Generic Views

Slide 24

Slide 24 text

Class Based Views • A class based analog of a view function • Method-based HTTP-verb dispatch • ... and that’s it

Slide 25

Slide 25 text

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):! ...!

Slide 26

Slide 26 text

Class Based Views • Automatic OPTIONS request handling • Automatic naïve HEAD request handling • Automatic HTTP 405 on unknown verbs

Slide 27

Slide 27 text

CB Generic Views • Uses Class Based View as a base • Creates analogs of the old generic views • Addresses limits of functional approach

Slide 28

Slide 28 text

Implementation choices https://code.djangoproject.com/wiki/ClassBasedViews

Slide 29

Slide 29 text

Ravioli • Goal: Replace function-based generics with class-based generics • Show what class-based approach can do • End point views built out of mixins

Slide 30

Slide 30 text

freakboy3742’s Ravioli Cooking 101

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Example: After CBGV class BookUpdateView(generic.UpdateView):! model = Book! ! def form_valid(self, form):! self.object.process()! return super(BookUpdateView, ! self).form_valid(form)!

Slide 42

Slide 42 text

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!

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

Documentation • Bad as originally released • Much better now • ccbv.co.uk • Still need framework decisions needed

Slide 45

Slide 45 text

Where to from here?

Slide 46

Slide 46 text

Have we solved the wrong problem?

Slide 47

Slide 47 text

Generic Views • Display a template • Display a single object • Display a list of objects • Display a form • Create, Update, Delete • Display a date archive

Slide 48

Slide 48 text

Modern web sites have different problems

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

API-driven UI in Django • APIs are easy • DRF, TastyPie and others. • Still need “views” • Navigation • Composition

Slide 52

Slide 52 text

All of this requires a strong base framework

Slide 53

Slide 53 text

Class-Based Views provide that framework

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

Doesn’t need to be part of Django core

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

Questions? http://djangoproject.org http://cecinestpasun.com [email protected] @freakboy3742