Slide 1

Slide 1 text

Bruno Renié djangocon.eu 2012 Class-based Views patterns & anti-patterns

Slide 2

Slide 2 text

New in Django 1.3 Better in Django 1.4

Slide 3

Slide 3 text

Controversy http://lukeplant.me.uk/blog/posts/djangos-cbvs-were-a-mistake/ http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html

Slide 4

Slide 4 text

The view contract in Django

Slide 5

Slide 5 text

# Your code def view(request): return HttpResponse('yay') urlpatterns = patterns('', r'^$', view) # Django callback, args, kwargs = resolve(request.path_info) response = callback(request, *args, **kwargs)

Slide 6

Slide 6 text

A view is a callable that takes a request and returns a response

Slide 7

Slide 7 text

Deprecation function-based generic views are being deprecated function-based views aren't going anywhere

Slide 8

Slide 8 text

Other class-based stuff

Slide 9

Slide 9 text

class Middleware(object): def process_request(self, request): ... Middleware

Slide 10

Slide 10 text

class PageAdmin(admin.ModelAdmin): def get_changelist(self, request, **kwargs): ... Admin

Slide 11

Slide 11 text

single instance shared across requests

Slide 12

Slide 12 text

from django.views import generic class Users(generic.ListView): ... users = Users.as_view() CBV api

Slide 13

Slide 13 text

as_view()?

Slide 14

Slide 14 text

class View(object): @classonlymethod def as_view(cls, **init): def view(request, *args, **kwargs): self = cls(**init) return self.dispatch(request, *args, **kwargs) return view

Slide 15

Slide 15 text

class View(object): @classonlymethod def as_view(cls, **init): def view(request, *args, **kwargs): self = cls(**init) return self.dispatch(request, *args, **kwargs) return view Thread-safety self.request self.args self.kwargs

Slide 16

Slide 16 text

Declarative vs. imperative

Slide 17

Slide 17 text

ccbv.co.uk

Slide 18

Slide 18 text

(biased) usage tips

Slide 19

Slide 19 text

Keep urls.py for URL definition Decorate in views.py

Slide 20

Slide 20 text

from django.utils.decorators import method_decorator def cbv_decorator(decorator): def _decorator(cls): cls.dispatch = method_decorator(decorator)(cls.dispatch) return cls return _decorator @cbv_decorator(login_required) class MyView(generic.ListView): pass Decorating

Slide 21

Slide 21 text

Decorating class MyView(generic.ListView): pass my_view = MyView.as_view() my_view = login_required(my_view)

Slide 22

Slide 22 text

Decorating from django.views.decorators.cache import cache_page class CacheMixin(object): cache_timeout = 60 def dispatch(self, *args, **kwargs): return cache_page(self.cache_timeout)( super(CacheMixin, self).dispatch )(*args, **kwargs) class CachedView(CacheMixin, ListView): cache_timeout = 100 @cyberdelia — https://gist.github.com/1231560

Slide 23

Slide 23 text

MRO Extend, don't override unless you're 100% sure of what you're doing

Slide 24

Slide 24 text

class MyView(generic.FormView): def get_initial(self): initial = super(MyViews, self).get_initial() initial.update({ 'foo': 'bar', 'other': 'thing', }) return initial

Slide 25

Slide 25 text

Case studies

Slide 26

Slide 26 text

Form processing class MyView(generic.FormView): def get_form_kwargs(self): kw = super(MyView, self).get_form_kwargs() kw['user'] = self.request.user return kw def form_valid(self, form): form.save() return super(MyView, self).form_valid(form)

Slide 27

Slide 27 text

Form processing class MyForm(forms.Form): def __init__(self, *args, **kwargs): self.user = kwargs.pop('user') super(MyForm, self).__init__(*args, **kwargs) def save(self): # self‐contained, user is known

Slide 28

Slide 28 text

Nested navigation class Level1(generic.TemplateView): template_name = 'level_1.html' def get_context_data(self, **kwargs): ctx = super(Level1, self).get_context_data(**kwargs) ctx['stuff'] = do_some_work() return ctx class Level2(Level1): template_name = 'level2.html' def get_context_data(self, **kwargs): ctx = super(Level2, self).get_context_data(**kwargs) ctx['other_stuff'] = level_2_work() return ctx

Slide 29

Slide 29 text

Drop-in features class CleverPaginator(object): paginate_by = 100 def get_count(self): raise NotImplementedError def get_paginate_by(self, queryset): count = self.get_count() if count > self.paginate_by * 1.5: return self.paginate_by return count class CountryView(CleverPaginator, ListView): def get_count(): return self.country.num_people

Slide 30

Slide 30 text

Registration

Slide 31

Slide 31 text

django-registration is great, but… I want more template variables I'm not using contrib.auth I want to send an SMS instead of an email …

Slide 32

Slide 32 text

Writing a custom backend is not as simple as subclassing the default views from le_social.registration import views class Register(views.Register): form_class = SMSRegistrationForm def send_notification(self): ... Sane, easily overridable defaults pip install django-le-social

Slide 33

Slide 33 text

Settings

Slide 34

Slide 34 text

Does it have to be that global? Is it a switch people will want to flip at any time? Could it be… a class attribute/method instead? I know, I'll add a setting

Slide 35

Slide 35 text

ACCOUNT_EXPIRES_IN from le_social.registration import views class Activate(views.Activate): expires_in = 3600 * 24 * 7 # 7 days

Slide 36

Slide 36 text

SOCIAL_AUTH_LOGIN_REDIRECT_URL SOCIAL_AUTH_BACKEND_ERROR_URL from le_social.twitter import views class Callback(views.Callback): def success(self, auth): ... def error(self, message): ...

Slide 37

Slide 37 text

CBVs are great but don't use them for everything

Slide 38

Slide 38 text

class Handler500(generic.TemplateView): template_name = '500.html'

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

class View(object): def dispatch(self, request, *args, **kwargs): handler = getattr(self, request.method.lower(), self.http_method_not_allowed) return handler(request, *args, **kwargs)

Slide 41

Slide 41 text

Fantastic API for shipping reusable views Good API for extending and plugging features in existing code

Slide 42

Slide 42 text

As a FBGV replacement: more power, less simplicity Don't limit yourself to Django's implementation. Use the base View and your creativity

Slide 43

Slide 43 text

@brutasse [email protected]