Professional Documents
Culture Documents
Ben Firshman
I’d like to bring your attention to a tale of neglect in the Django community
Models
Something missing...
Models
Templates
Something missing...
Models
Templates
Something missing...
Views
How views work - generic views in particular - have barely been touched since Django was
released.
I’m going to explain one problem with views that needs fixing
View
View
response
BUT - they are very inflexible. How many people have used a generic view, then almost
immediately found they have to rewrite it?
We’ve had to rewrite the functionality of our generic view. This could be even more
complicated if we were using other generic view features
Say this view is a view in a reusable application - we’ll have to rewrite it again to change a
small bit of functionality
newforms-admin
admin.site.register(Book, BookAdmin)
This is a simplified example - coincidently the same one Alex used in his talk earlier. It’s
overriding a hook in the admin that only lets users edit objects they’ve created.
Almost every decision made in the admin is done in a method that you can override. This
means you don’t have to rewrite the whole admin just to add an extra bit of functionality
Back in 2008, Joseph Kocherhans applied these ideas to views
request
View
response
With our simple model of views - I said views are a function that takes a request and returns
a response.
I lied - they are a *callable* that takes a request and returns a response.
__call__()
You can define this magic method on objects that gets called if you call the object like a
function
class PostView(DetailView):
queryset = Post.objects.all()
Joseph’s idea let you write views like this. This is equivalent of the generic view we had
earlier.
DetailView has __call__() method which takes a request and primary key from the URL
dispatcher
class PostView(DetailView):
queryset = Post.objects.all()
Now, say we want to extend it like we did before. We don’t have to rewrite the generic view,
just override a method
This is not a great example because its the same length as the simple function we had before.
But - views in functions can get very hairy very quickly - you can see how this method will
scale. You can even inherit from this class again to create a view with slightly different
functionality
There are also generic views like this for listing objects; creating, editing and deleting objects
with forms; and listing objects within certain time periods. There are even views for just
displaying something in a template, which all these views inherit from
from jingo import render_to_string, env
class JinjaMixin(object):
def render(self, names=None, context=None):
template = env.select_template(names)
return render_to_string(
self.request, template, context)
We can also do clever stuff. Mixins are really handy to alter the functionality of class-based
views.
This, for example, is a Mixin that makes any view render with Jinja instead of Django’s
templates.
TemplateView defines a method for rendering to a Django template. We can override this to
render to a Jinja template instead
class PostView(JinjaMixin, DetailView):
queryset = Post.objects.all()
If we add that mixin to our PostView we made earlier, it will now render with Jinja templates
class JsonMixin(object):
def render_to_response(self, names=None, context=None):
if self.kwargs.get('format') == 'json':
return self.get_response(
self.get_resource(context),
mimetype='application/json'
)
return super(JsonMixin, self).render_to_response(
names, context)
Outputs JSON as well as templates given a format keyword argument from the URL
Overriding a different method to the jinja example because we need to set the mimetype of
the response, but the idea’s the same
from django.core import serializers
Now we have a view that can output to JSON as well as templates. All the logic we’ve written
to output data to templates can be reused.
Many API tools for Django mean you have to use different URL namespaces, which isn’t
RESTful
from django.core import serializers
and of course - if we hook in our Jinja plugin to this view, we have a view that outputs to Jinja
templates and JSON
django.views
Not just for generic views - this can become the standard way of writing views.
If everybody builds upon a single base view, cool stuff like Jinja mixins will work everywhere,
including reusable apps
That’s not saying we’re getting rid of the old way of writing views though - views are still just
a callable that takes a request and a response
class PostAdmin(admin.ModelAdmin):
def get_urls(self):
return patterns('',
(r'^custom/$', self.custom_view),
) + super(PostAdmin, self).get_urls()
It lets you write urlconfs which can do clever routing, and subclass and extend them
Simon Willison did a similar thing with django openid, but with a neater API.
You include a consumer into the urlconf like you include the admin interface, but it iterates
over all the methods on itself and finds ones with a url regex defined.
If somebody were to develop a generic way of writing class-based urlconfs, and set some
conventions, it could be a really useful pattern. Maybe the urlconfs in Django core could be
smarter too......
This class-based view stuff is all well and good, but ticket 6735 has been around for almost 3
years. I’d like to cover some of the history of this and what we need to do to get this into
Django core.
As Eric mentioned yesterday, class-based views need conventions. Once it’s a part of Django,
this will be the convention that views can be built upon.
It started with Joseph’s patches, which had a number of people working on it, but was never
finished. After 1.0, Jacob KM worked on it for 1.1, but again, never finished it.
Since then, it has drifted, and the wheel has been reinvented many times.
The problem is, class-based views are prime bike-shedding material. The details of
implementing them have been argued over many flame wars on django-dev.
It’s incredibly hard to decide on how it should work because it is one huge naming problem.
It’s designing an API - choosing method names.
Thread safety was another sensitive topic.
(explain)
This is in fact a tiny part of how class-based views work. If you don’t like how it works - just
write a mixin that changes it.
http://github.com/bfirsh/
django-class-based-views
Here is my stab at an API. It is based on Joseph and Jacob’s initial implementation, with work
from David Larlet and inspiration from Andrew Godwin.
http://github.com/bfirsh/
django-class-based-views
Please use, fork and improve. Docs and tests need writing
Questions?
@bfirsh
Credits
http://www.flickr.com/photos/gerlos/3119891607/
http://www.flickr.com/photos/buttersweet/33684613/