Django was always great for developers out of the box, but creating friendly admin interfaces was always a little too much work. This post explains why I now consider using Wagtail to make this task easier.

Over the years I've tried pretty much every available Django CMS out there. I was never satisfied with any of them because Django's admin interface is just good enough out of the box. The overhead of using an additional app often wasn't worth it as the built-in admin will work for many use cases. Sometimes you need something that is a little more user friendly and more flexible though, and in such cases I often implemented a custom admin app. That's surprisingly easy to do with Django's generic views, ModelForms, etc. Some websites are also supposed to have more or less free form pages that don't really fit into any specific model, and a CMS app can help in such situations. After a client asked for a site with some free form sections I looked into CMS solutions again and found Wagtail. First impression: the admin interface looks very nice and is easy enough to customize. That's already good enough to consider it. After some more work with it I found something that really makes Wagtail stand out, Streamfields. Matthew Wescott, Wagtail's lead technical developer explains the motivation for creating them, and why Rich Text Fields are a poor solution for most editing problems.

More importantly, though, we're losing the semantic value of the content. If you want to insert a Google map on your page, you can get a blob of HTML from Google [...] But the actual meaningful information - "a map centred on the postcode OX7 3EW" - is buried irretrievably within that blob of HTML. Matthew Westcott: Rich text fields and faster horses

Creating huge blobs of HTML is probably something you dislike if you're using Django. To solve this problem many CMS allow you to have different "blocks" of content on one page that can use many different data models. But the user interface is usually clunky, this is something Streamfields fix, and that's the lure of using Wagtail. See the video below for an introduction, the interface is even more impressive when you start using it yourself.



This makes the admin interface look very neat and organized, adding all kinds of content is very easy, and most semantic information is preserved.

My Streamfield

These are the choices in my own Streamfield, the one I used to create this post. It only uses one snippet and two custom blocks and integrates them into the Streamfield: quotes, source code, and pastes. The pastes are an old model I used somewhere else on this site. The other elements like header, image and link come with Wagtail core and just need a little configuration.

A simple block

Alright, let's look at some code. The example below is how I implemented a quote block that gets rendered as blockquote. See the Wagtail documentation for more info.



from wagtail.wagtailcore.fields import StreamField from wagtail.wagtailcore import blocks from wagtail.wagtailcore.models import Page class QuoteBlock ( blocks . StructBlock ): quote = blocks . TextBlock () source = blocks . CharBlock () link = blocks . URLBlock () author = blocks . CharBlock () class Meta : icon = 'openquote' template = 'wt/quote.html' class BlogPage ( Page ): body = StreamField ([ ( 'Heading' , blocks . CharBlock ( classname = "full title" )), ( 'Paragraph' , blocks . RichTextBlock ()), ( 'Quote' , QuoteBlock ()), ])

This example contains the quote block, and a simple Page model that has the quote block in its Streamfield. The wt/quote.html template context receives a variable called value that can be used to access the quote properties.

A simple snippet

The example below is a quote implemented as its own model and snippet. This has the benefit of making quotes reusable, not very useful in this example, but it might be useful for all kinds of data. Even quotes if you're creating scientific content for example. Having a separate model also makes it very easy to create views that show individual quotes, or lists of them. I had built this snippet first but decided later that a block like shown above would be more appropriate for my use case.



from django.db import models from wagtail.wagtailsnippets.models import register_snippet from wagtail.wagtailsnippets.blocks import SnippetChooserBlock from wagtail.wagtailcore.models import Page from wagtail.wagtailcore.fields import StreamField @register_snippet class Quote ( models . Model ): quote = models . TextField () source = models . CharField ( max_length = '255' ) link = models . URLField ( blank = True ) author = models . CharField ( max_length = '255' ) def __unicode__ ( self ): return " %s : %s ..." % ( self . author , self . quote [: 50 ]) class QuoteChooserBlock ( SnippetChooserblock ): class Meta : template = "myblog/quote.html" class BlogPage ( Page ): body = StreamField ([ # [...] ( 'Quote' , QuoteChooserBlock ( Quote , icon = 'openquote' )), ])

This QuoteChooserBlock can easily be added to any Streamfield so that any editor can use it. The template variable value is used to render an instance on the frontend. This has the downside that the context object name can conflict with existing templates, so here's how to change the template variable:

class QuoteChooserBlock ( SnippetChooserBlock ): TEMPLATE_VAR = 'quote' class Meta : template = 'myblog/quote.html'

This attribute is undocumented and I had to read the Wagtail source code to find it, but it works for now. If you want total control there's also the render method you can use like this:

class QuoteChooserBlock ( SnippetChooserBlock ): def render ( self , instance ): return render_to_string ( 'myblog/quote.html' , { 'quote' : instance })

Of course this example doesn't do anything useful compared to the code above, but I'm already using a similar approach for things like syntax highlighting and sections of ReStructured Text.



Conclusion