• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

Five class-based views everyone has written by now

on

  • 7,615 views

 

Statistics

Views

Total Views
7,615
Views on SlideShare
7,311
Embed Views
304

Actions

Likes
8
Downloads
32
Comments
1

3 Embeds 304

http://lanyrd.com 211
http://olcomputey.com 52
http://olcomputey.com:8000 41

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1 previous next

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • There's a (possibly) more Django idiomatic approach to the editing stuff (model formsets and the like) at http://pypi.python.org/pypi/django-extra-views by Andrew Ingram.

    The DetailListView concept I've pared down a little and submitted as an example to the Django documentation, so it may appear in some form there at some point.

    Permissions still waiting someone to do it nicely. It doesn't help that the 'official' way of doing decorators decorates the dispatch method, or decorates the entire class and both get magically applied to the generated view function…neither of which have access to the kinds of things we need to be able to implement a SingleObjectPermissionMixin. And we still don't have arbitrary raisable exceptions in Django yet.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Five class-based views everyone has written by now Five class-based views everyone has written by now Presentation Transcript

    • Five class-based views everyone has written by now James Aylett artfinder.comWednesday, 2 November 11Django 1.3, released in March, introduced CLASS BASED VIEWS, which are intended to bemake writing views easy and delightful. They replaced the function-based generic views, andtherefore must be better, or something.
    • from  django.views.generic  import  * class  AWub(DetailView):    model  =  Wub class  Wubs(ListView):    model  =  WubWednesday, 2 November 11We’ll pretend you can’t pass parameters in urlconfs, because that’s icky and also misses thepoint.
    • from  django.views.generic  import  * class  AWub(DetailView):    model  =  Wub class  Wubs(ListView):    model  =  Wub    def  get_queryset(self):        return  Wub.objects.exclude(            hidden=True        )Wednesday, 2 November 11The point is that you can override small bits of the ways the views work, to refine them foryour application. Add further context for your templates, change the queryset and so on.There are conventional places to put templates, and for naming template context members.Upshot: little code, big effect.
    • from  django.views.generic  import  * class  NewWub(CreateView):    model  =  Wub class  DeleteWub(DeleteView):    model  =  Wub class  UpdateWub(UpdateView):    model  =  WubWednesday, 2 November 11Generic views had ways of editing, creating and deleting, so you get that too. Write a suitabletemplate, and EVERYTHING else is automatic, using a default form. If you want a differentform, you can override that easily enough. Sounds good, right?
    • Wednesday, 2 November 11Unfortunately you are about to enter a world of pain. Let’s consider an example that isn’tcompletely trivial.
    • from  django.db  import  models class  WubFlock(models.Model):    name  =  models.CharField(…) class  Wub(models.Model):    name  =  models.CharField(…)    flock  =  models.ForeignKey(        WubFlock,  related_name=‘wubs’)Wednesday, 2 November 11Okay, so we have a flock of Wubs. We’re building a wub management interface, so we’ll needto create new wubs within a flock. Except…oh no. You can’t do that.
    • from  django.views.generic  import  * class  NewFlockWub(CreateView):    model  =  Wub    #  what  goes  here?Wednesday, 2 November 11Okay, so you want to customise the form so it’ll exclude the “flock” field, but set it pre-saveto the flock this wub is going to be in. (Yes you could have a dropdown of all flocks, but thenyour designer will murder you in your sleep.)
    • from  django.views.generic  import  * class  NewFlockWub(CreateView):    model  =  Wub    form_class  =          SomethingSomethingFormWednesday, 2 November 11Okay, so we’ll define the form somewhere. Only…
    • from  django.views.generic  import  * class  NewFlockWub(CreateView):    model  =  Wub    form_class  =          SomethingSomethingForm    #  erm,  but  we  need  to  get    #  the  flock  object  for  the  formWednesday, 2 November 11You want to figure out the flock from the URL, say “/flock/my-awesome-wubs”. DetailViewdoes this, and like all class-based views is built up of composable little pieces, so you couldbring in SingleObjectMixin, which provides get_object to do this. Then we could overrideget_form_class to set up the form dynamically. But this behaviour is generic, so…
    • from  moreviews  import  * class  NewFlockWub(BoundCreateView):    model  =  Wub    bound_field  =  ‘flock’    queryset  =  WubFlock.objects.all()Wednesday, 2 November 11If your BoundCreateView isn’t this easy, you’re doing it wrong. “Bound” because the Wub isbound to the WubFlock.
    • class  DeleteWub(DeleteView):    model  =  Wub class  F(forms.ModelForm):    class  Meta:        model  =  Wub        exclude  =  (‘flock’,) class  UpdateWub(UpdateView):    model  =  Wub    form_class  =  FWednesday, 2 November 11We don’t have to worry about binding for deleting, and for updating we just ensure we don’tchange the “flock” field.
    • Wednesday, 2 November 11But what about the WubFlock? We should be able to create it AND ITS WUBS in one go. Intraditional function views you’d do this with forms and formsets within the same request. Sowe want to do the same thing in class-based views.
    • class  ProcessMultipleFormsMixin:    """Modify  GET  and  POST  behaviour   to  construct  and  process  multiple   forms  in  one  go.  Theres  always  a   primary  form,  which  is  a  ModelForm. By  the  time  secondary  forms  are   saved,  self.new_object  on  the  view   will  contain  the  primary  object,  ie   the  object  that  the  primary  form   operates  on."""Wednesday, 2 November 11This isn’t one of the five classes, this is just a mixin. It’s not named perfectly, becausealthough it DOES process multiple forms at once, it assumes one is the main form. Thisallows us to save the PRINCIPAL object, and then leaves a reference for all the other forms touse.
    • from  django.views.generic  import  * class  NewFlock(MultiCreateView):    model  =  WubFlock    forms_models  =  [        {            ‘model’:  Wub,            ‘extra’:  1,        }    ]Wednesday, 2 November 11This syntax is a little opaque, but it didn’t seem worth creating yet more classes just ashelpers when we have lists and dictionaries. The exclude of the project field in theModelForm for making little Wubs is taken care of for you.
    • from  django.views.generic  import  * class  UpdateFlock(MultiUpdateView):    model  =  WubFlock    forms_models  =  [        {            ‘model’:  Wub,            ‘extra’:  1,            ‘can_delete’:  True,        }    ]Wednesday, 2 November 11extra and can_delete here are both passed through to modelformset_factory. You can also setform inside the dictionary so you don’t just get a default ModelForm but can customise toyour heart’s content.
    • from  django.views.generic  import  * class  UpdateFlock(MultiUpdateView):    …    def  get_forms(self):        class  WubInlineForm(ModelForm):            def  save(self):                #  perhaps  we  default  the                  #  name  based  on  the  flock?        self.forms_models[0][‘form’]              =  WubInlineForm        return  super(...)()Wednesday, 2 November 11You can even dynamically adjust things if you so choose. In fact, you can avoid forms_modelsby implementing get_forms directly if you prefer.
    • from  django.views.generic  import  * class  DeleteFlock(DeleteView):    model  =  WubFlockWednesday, 2 November 11And of course deleting a flock will delete its wubs via the evil of the ORM’s implementingCASCADE DELETE itself.
    • Wednesday, 2 November 11There’s also a variant which will allow you to create an object bound to another but whichitself has children bound to it. It’s imaginatively called MultiBoundCreateView. That’s not oneof the five, that’s a bonus one. However now we need to think about non-editing views again,because we’ve still got a problem.
    • from  django.views.generic  import  * class  Wubs(ListView):    model  =  Wub class  Flock(DetailView):    model  =  WubFlockWednesday, 2 November 11This is fine until you have a flock with three thousand wubs in. And wubs are very gregarious.
    • from  django.views.generic  import  * class  Wubs(ListView):    model  =  Wub    paginate_by  =  10 class  Flock(DetailView):    model  =  WubFlockWednesday, 2 November 11ListView has pagination built in. Wouldn’t it be nice if we could do that for the CHILDRENbound to the object in a DetailView?
    • from  django.views.generic  import  * from  moreviews  import  * class  Wubs(ListView):    model  =  Wub    paginate_by  =  10 class  Flock(DetailListView):    model  =  WubFlock    paginate_by  =  10Wednesday, 2 November 11This is the fourth view. Warning: this exists, deep within the Artfinder codebase, but isn’t re-usable yet.
    • Wednesday, 2 November 11We’re not finished yet.
    • From:  Your  Boss  <phb@myco.co.com> To:  You  <peon@myco.co.com> Subject:  Permissions Yo  dawg.  I  was  over  at  Big  Client   Co  this  morning  and  noticed  that   they’re  able  to  edit  the  WubFlock   for  the  Sinister  Government   Agency.  This  contains  proprietary   Wub  technology,  and  geese,  so  THIS   SHOULD  NOT  BE  ALLOWED.Wednesday, 2 November 11Maybe your site allows everyone to see and do everything. Most don’t. What you used to dowith function views was to check the permission at the top of the view and return a 403. Inclass-based views you need to do that around get_object, which gives you direct access tothe object.
    • def  editable():    def  decorator(fn):        @wraps(fn,  assigned=available_attrs(fn))        def  _wrapped_fn(self,  *args,  **kwargs):            obj  =  fn(self,  *args,  **kwargs)            if  obj.editable(self.request.user):                return  obj            else:                raise  HttpForbidden()        return  _wrapped_fn    return  decorator class  _UpdateFlock(UpdateFlock):    get_object  =          editable(UpdateFlock.get_object)Wednesday, 2 November 11Oh god oh god I want to die. Ignore that decorators make most people’s heads hurt, justLOOK AT THAT CODE AT THE BOTTOM.
    • class  _UpdateFlock(UpdateFlock):    get_object  =          editable(            UpdateFlock.get_object        )Wednesday, 2 November 11There is no way this is going to look nice, no matter how many lines we wrap it onto. So thisis the fifth class…that I’m really hoping someone has written. I want to write something likethe following.
    • class  _UpdateFlock(UpdateFlock):    def  allowed(self):        return  self.object.editable(            self.request.user        )Wednesday, 2 November 11Which is basically the same but not ugly. I guess what we actually want here is a permissions-activating mixin.
    • class  _UpdateFlock(    UpdateFlock,    SingleObjectPermissionsMixin):    def  allowed(self):        return  self.object.editable(            self.request.user        )Wednesday, 2 November 11
    • class  SingleObjectPermissionsMixin(    object):    def  get_object(self):        obj  =  super(            SingleObjectPermissionsMixin,            self).get_object()        if  not  self.allowed(obj):            raise  HttpForbidden()        return  obj    def  allowed(self):        raise  NotImplementedError()Wednesday, 2 November 11So this would be the fifth class. I haven’t written this, but I assume someone has. I suspect asI have it here, there are lots of problems, not least that HttpForbidden doesn’t exist in Djangoitself and so you’re dependent on something else to not only provide it but catch it inmiddleware and do something sensible with it.
    • Summary • BoundCreateView for making Wubs • MultiCreateView for making WubFlocks • MultiUpdateView to update WubFlocks • DetailListView for paginating child objects • SingleObjectPermissionsMixin is mythicalWednesday, 2 November 11The first three are written, but Ben caught me by surprise by asking me to talk, so they aren’tpackaged and they’re not directly tested. DetailListView needs extracting from well-usedinternal code. The permissions stuff is entirely speculative; maybe we don’t need it.
    • Questions? • James Aylett • @jaylett • artfinder.com • http://bit.ly/djugl-artWednesday, 2 November 11