Author Archives: Tom de Simone

Django Class-Based Generic Views: tips for beginners (or things I wish I’d known when I was starting out)

Django is renowned for being a powerful web framework with a relatively shallow learning curve, making it easy to get into as a beginner and hard to put down as an expert. However, when class-based generic views arrived on the scene, they were met with a lukewarm reception from the community: some said they were too difficult, while others bemoaned a lack of decent documentation. But if you can power through the steep learning curve, you will see they are also incredibly powerful and produce clean, reusable code with minimal boilerplate in your views.py.

So to help you on your journey with CBVs, here are some handy tips I wish I had known when I first started learning all about them. This isn’t a tutorial, but more a set of side notes to refer to as you are learning; information which isn’t necessarily available or obvious in the official docs.

Starting out

If you are just getting to grips with CBVs, the only view you need to worry about is TemplateView. Don’t try anything else until you can make a ‘hello world’ template and view it on your dev instance. This is covered in the docs. Once you can handle that, keep reading the docs and make sure you understand how to subclass a ListView and DetailView to render model data into a template.

OK, now we’re ready for the tricky stuff!

Customising CBVs

Once you have the basics down, you will find that most of your work revolves around subclassing the built-in class-based generic views and overriding one or two methods. At the start of your journey, it is not very obvious what to override to achieve your goals, so remember:

  • If you need to get some extra variables into a template, use get_context_data()
  • If it is a low-level permissions check on the user, you probably want dispatch()
  • If you need to do a complicated database query on a DetailView, ListView etc, try get_queryset()
  • If you need to pass some extra parameters to a form when constructing it via a FormView, UpdateView etc, try get_form() or get_form_kwargs()

ccbv.co.uk

If you haven’t heard of ccbv.co.uk, go there and bookmark it now. It is possibly the most useful reference out there for working with class-based generic views. When you are subclassing views and trying to work out which methods to override, and the official docs just don’t seem to cut it, ccbv.co.uk has your back. If it wasn’t for that site, I think we would all be that little bit grumpier about using CBVs.

Forms

CBVs cut a LOT of boilerplate code out of the process of writing forms. You should already be using ModelForms wherever you can to save effort, and there are generic class-based views available (CreateView/UpdateView) that allow you to plug in your ModelForms and reduce your boilerplate code even further. Always use this approach if you can. If your form does not map to a particular model in the database, use FormView.

Permissions

If you want to put some guards on your view e.g. check if the user is logged in, check they have a certain permission etc, you will usually want to do it on the dispatch() method of the view. This is the very first method that is called in your view, so if a user shouldn’t have access then this is the place to intercept them:

1
2
3
4
5
6
7
8
9
10
from django.core.exceptions import PermissionDenied
from django.views.generic import TemplateView
 
class NoJimsView(TemplateView):
    template_name = 'secret.html'
 
    def dispatch(self, request, *args, **kwargs):
        if request.user.username == 'jim':
            raise PermissionDenied # HTTP 403
        return super(NoJimsView, self).dispatch(request, *args, **kwargs)

Note: If you just want to restrict access to logged-in users, there is a @require_login decorator you can add around the dispatch() method. This is covered in the docs, and it may be sufficient for your purposes, but I usually end up having to modify it to handle AJAX requests nicely as well.

Multiple inheritance

Once you start subclassing and overriding generic views, you will probably find yourself needing multiple inheritance. For example, perhaps you want to extend your “No Jims” policy (see above) to several other views. The best way to achieve this is to write a small Mixin and inherit from it along with the generic view. For example:

1
2
3
4
5
6
7
8
9
10
11
class NoJimsMixin(object):
    def dispatch(self, request, *args, **kwargs):
        if request.user.username == 'jim':
            raise PermissionDenied # HTTP 403
        return super(NoJimsMixin, self).dispatch(request, *args, **kwargs)
 
class NoJimsView(NoJimsMixin, TemplateView):
    template_name = 'secret.html'
 
class OtherNoJimsView(NoJimsMixin, TemplateView):
    template_name = 'other_secret.html'

Now you have entered the world of python’s multiple inheritance and Method Resolution Order. Long story short: order is important. If you inherit from two classes that both define a foo() method, your new class will use the one from the parent class that was first in the list. So in the above example, in your NoJimsView class, if you listed TemplateView before NoJimsMixin, django would use TemplateView’s dispatch() method instead of NoJimsMixin’s. But in the above example, not only will your NoJimsMixin’s dispatch() get called first, but when you call super(NoJimsMixin, self).dispatch(), it will call TemplateView’s dispatch() method. How I wish I had known this when I was learning about CBVs!

View/BaseView/Mixin

As you browse around the docs, code and ccbv.co.uk, you will see references to Views, BaseViews and Mixins. They are largely a naming convention in the django code: a BaseView is like a View except it doesn’t have a render_to_response() method so it won’t render a template. Almost all Views inherit from a corresponding BaseView and add a render_to_response() method e.g. DetailView/BaseDetailView, UpdateView/BaseUpdateView etc. This is useful if you are subclassing from two Views, because it means you can choose which one renders the final output. It is also useful if you want to render to JSON, say in an AJAX response, and don’t need HTML rendering at all (in this case you’d need to provide your own render_to_response() method that returns a HttpResponse).

Mixin classes provide a few helper methods, but can’t be used on their own, as they are not full Views.

So in short, if you are just subclassing one thing, you will usually subclass a View. If you want to manually render a non-HTML response, you probably need a BaseView. If you are inheriting from multiple classes, you will need a combination of some or all of View, BaseView and Mixin.

A final note on AJAX

Django is not particularly good at serving AJAX requests out of the box, and once you start trying to use CBVs to do AJAX form submissions, things get quite complicated.

The docs offer some help with this in the form of a Mixin you can copy and paste into your code, which gives you JSON responses instead of HTML. You will also need to pass CSRF tokens in your POST requests, and again there is an example of how to do this in the docs.

This should be enough to get you started, but I often find myself having to write some extra Mixins, and that is before even considering the javascript code on the front end to send requests and parse responses, complete with handling of validation and transport errors. Here at Isotoma, we are working on some tools to address this, which we hope to open-source in the near future. So watch this space!

Conclusion

In case you hadn’t worked it out, we at Isotoma are fans of Django’s class-based generic views. They are definitely not straightforward for newcomers, but hopefully with the help of this article and other resources (did I mention ccbv.co.uk?), it’ll be plain sailing before you know it. And once you get what they’re all about, you won’t look back.

About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.

Content types and Django CMS

Screenshot of the new ENB website

The new ENB website

One of our latest projects to go live is a new website for the English National Ballet. Part of a major rebrand, we completely replaced their old PHP site with a new content-managed site powered by Django CMS.

Django CMS is very flexible, largely due to its minimalistic approach. It provides no page templates out of the box, so you can construct your HTML from the ground up. This is great if you want to make a CMS with a really strong design, because there is very little interference from the framework. However, its minimalistic approach also means that you sometimes have to write extra code to tie all the content together.

A good example of this is content types. In Django CMS, there is only one content type: Page. It has certain fields associated with it e.g. title, slug, published. Any other information that appears on a page comes courtesy of plugins. The default Django CMS plugins give you everything you need to add arbitrary text, images and video to a page. But what if you want more fields for your page? Let’s say, for example, you are representing a ballet production and you want category, thumbnail and summary text fields, which don’t appear on the page itself but are needed for listings elsewhere on the site?

We decided to create a special “metadata” plugin to be added to the production pages, that would only be visible to content editors and not end users. This was seen as the best solution that achieved our goal while maintaining a decent user experience for the editors.

The plugin model looks something like this:

1
2
3
4
class ProductionDetails(CMSPlugin):
    summary = models.CharField(max_length=200) # Short summary, shown in listings
    image = FilerImageField() # Thumbnail image, shown in listings
    audiences = models.ManyToManyField(Audience) # Categorisation

Note the use of django-filer for the image field. This is simply the best add-on I have encountered for dealing with image uploads and the inevitable cropping and resizing of said images. You can also use cmsplugin-filer (by the same author) to replace the standard image plugin that comes with Django CMS.

Now querying the database for, say, the first 10 productions for a family audience (audience id 3) is as simple as:

ProductionDetails.objects.filter(audiences=3, placeholder__page__published=True)[:10]

So now we have a plugin model that we can query, and we don’t need a template as we don’t want it to appear on the actual page, right? Wrong. We still want to provide a good user experience for the editors, and this includes looking at a page in edit mode and being able to tell whether the page already has the plugin or not. So we use request.toolbar.edit_mode in the template to decide whether to render the plugin:

1
2
3
4
5
6
7
8
9
{% load thumbnail %}
 
{% if request.toolbar.edit_mode %}
<div id="production-details">
 <img src="{% thumbnail instance.image 100x100 crop upscale subject_location=instance.image.subject_location %}" />
 <p>Summary: {{ instance.summary }}</p>
 <p>Audiences: {{ instance.audiences.all|join:', ' }}</p>
</div>
{% endif %}

Now this information will only appear if an editor has activated the inline editing mode while looking at the page. If they look at the page and the information is missing, they know they need to add the plugin!

This solution works quite well for us, although it is still fairly easy to create a page and forget to give it any metadata. Ideally it would be mandatory to add a metadata plugin. Perhaps the subject of a future blog post!

About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.

Running a Django (or any other) dev instance over HTTPS

Being able to run your dev instance over HTTPS is really useful: you might spot some weird bug that would have bitten you in production, and if you do find one, you can debug it much more easily. Googling for this subject resulted in several different tutorials using stunnel, but all of them broke in some way on my machine running Ubuntu Maverick. So here is how I got stunnel working – perhaps it will help someone else too:

sudo aptitude install stunnel
sudo su -
cd /etc
mkdir stunnel
cd stunnel
openssl req -new -x509 -days 365 -nodes -out stunnel.pem -keyout stunnel.pem
openssl gendh 2048 >> stunnel.pem
chmod 600 stunnel.pem
logout
cd

Now create a file called dev_https with the following text:

pid=
foreground=yes
debug = 7

[https]
accept=8443
connect=8000
TIMEOUTclose=1

Note: this assumes your web server is running on port 8000. If it’s not, change the value of “connect” to the appropriate port.

Finally, run:

sudo stunnel4 dev_https

Now if you go to https://localhost:8443/, you should see your HTTPS-enabled dev instance!

Note: To properly simulate a HTTPS connection in Django, you should also set an environment variable HTTPS=on. Without this, request.is_secure() will return False. You could set it at the same time as starting your dev instance e.g:

HTTPS=on python manage.py runserver

About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.

HTML5 input type=number and decimals/floats in Chrome

As you may already know, HTML5 has introduced a number of new input types, one of which is the “number” type. As you might expect, this is a form field which accepts numeric input. So what happens in Chrome with the following HTML when we try to enter a decimal (floating point) number and submit the form?

<input type="number" />

Answer: Chrome pops up a validation error:

Decimal number failing validation

So what’s going on here? Is it a bug? It doesn’t fail in Firefox.

Well actually it’s not a bug; the form field is behaving as defined by the W3C. Numeric input fields can take additional attributes “min” and “step”, which constrain the range of values allowed in your input. The “min” attribute is fairly obvious: it’s the minimum value your number can be. The “step” attribute is less intuitive: by playing with different values, you would most likely work out that it controls the increase/decrease when clicking the up/down buttons on the field. If you input the number 1 and click the up arrow, it will increase to 2. This is because the default step is 1. So far, so obvious. However, the step attribute also determines which values are valid, so a step of 1 means you can enter 1, 2, 3 etc. and a step of 2 means you can enter 2, 4, 6 etc, and when you click the up/down buttons the number will increase/decrease by 2 each time, but entering 3 or 5 in the box will cause a validation error. You can also use a decimal value: for example, a step of 0.3 will allow values such as 0.3, 0.6, 0.9 etc, but not 1 or 2.

But what if you want all the numbers to be valid, integers and decimals alike? In this case, set step to “any”:

<input type="number" step="any" />

Now you don’t get a validation error. Yay! Also note that if you only want to accept positive numbers, you’ll want to add min=”0″.

So the lesson here is that the “step” attribute is linked to both the up/down buttons and the range of values allowed in the field. When step=”any”, the up/down buttons will increase/decrease the number by 1. As far as I can tell, there is no way to have step=”any” and an increase/decrease of more (or less) than 1 when clicking the up/down buttons. Feel free to enlighten me in the comments though!

About us: Isotoma is a bespoke software development company based in York and London specialising in web apps, mobile apps and product design. If you’d like to know more you can review our work or get in touch.