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

Class-based Views: patterns and anti-patterns

Class-based Views: patterns and anti-patterns

Bruno Renié

June 04, 2012

More Decks by Bruno Renié

Other Decks in Programming


  1. # 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)
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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)
  7. 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
  8. 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
  9. 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
  10. 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 …
  11. 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
  12. 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
  13. 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)
  14. As a FBGV replacement: more power, less simplicity Don't limit

    yourself to Django's implementation. Use the base View and your creativity