Class-based Views with Django-Baseviews
I've always loved the concept of class-based views because views often become repetitive. There are a lot of common operations in a view that work best when they are defined once and reused. Class inheritance, in my opinion, is the best way to keep views DRY and allow you to focus on what makes each view different.
At first, I used to break out pieces of functionality into their own functions in the views.py file. This worked okay, but I had to pass a lot of data around in the arguments and I had to do a lot of imports if I had functions from other app's views that I wanted to use in this app. This quickly became tedious.
After seeing django-haystack use class-based views and really loving the way I could customize their behavior for my own project, I decided to start building some base views of my own to build on. While working on new Pegasus News apps, I started building up a set of class-based views that helped me stop repeating everything that the views had in common. Once the base views were in place, the process of creating new views for my apps because a little quicker and more organized.
I've received permission to open-source the app, so it's now out on Github. I've already received a few contributions (from Justin Quick, Jannis Leidel, and Chuck Harmston - thanks guys!) and I'd be happy to receive more. I've been using the project in production on Pegasus News since June, and it has worked well so far.
How I'm using django-baseviews
For the apps I'm using baseviews with on Pegasus, I start out with a BasicView for the index. Then, for any views that deal with a specific model instance I create a base class for the app that takes the slug or id from the url pattern, grabs the object, and adds it to the class instance.
:::python
class ItemView(BasicView):
def __init__(self, request, slug):
self.item = get_object_or_404(Item, slug=slug)
super(ItemView, self).__init__(request)
I then subclass the item-specific view as the base for any views that deal with a specific item. This gives me the self.item attribute on my subclasses without having to repeat the get_object_or_404
call for every view. This is very a basic example, and it's just the start of what you can do.
For a real world example - in one of my apps, I had a view that simply displayed all instances of a model. Later on after the app was launched, I found out that I needed to create a page that showed all the instances of that model within a specific category. All I had to do was move the queryset creation out into its own method, create a quick subclass of my original view, and override the queryset method on the subclass to filter by the requested category.
Plans for continued development
After watching Ben Firshman's Alternative Views talk from Djangocon US, I've learned about the concept of mixins - using Python's multi-inheritance to add small pieces of functionality. For example, I could create a mixin that renders everything to JSON, or one to render in XML, or one for YAML. Then, when subclassing FormView I could add the JSON mixin to the front of the new class's bases::
:::python
class ItemFormView(RenderJson, FormView):
...
Assuming that all RenderJson does is override the render method, this would allow the RenderJson class to handle rendering and FormView to handle all the other methods.
I'd also like to take a look at Jacobian's class-based generic views and do something similar for baseviews.
I'm going to keep building up baseviews as I use it for other projects, and I encourage you to contribute any view classes to baseviews that you think would help speed up development for others. Once I get enough view classes, I'll start to split views.py
up into different files based on the type of views contained.
The image at the top of this post is from Natalie Dee, a funny and exceedingly irreverent web comic.