Querysets for Wagtail field panels

When creating the editing interface for CMS editors, I always try to ensure that the options presented are the only ones available.

This reduces cognitive load for the editor (there are no wrong choices), and makes our data less reliant on conditionals in python or in our templates (there are no wrong data).

I aim to create relationships between items where possible, to avoid duplicated or divergent content. In most cases I usually use simple taxonomies and term names.

A recent project required an InlinePanel with a foreign key relationship to a snippet-based taxonomy. To restrict the terms to particular page types, the terms have a reverse relationship to the associated ParentalKey page type for the InlinePanel (stored as a class name string).

This gives us the ability to query the taxonomy, and grab a queryset that only returns the valid terms for that page type.

Unfortunately, there's no way to pass that queryset to the regular Wagtail FieldPanel. You can pass choices, but that's not the same thing, and it isn't meant to be dynamic.

We can, however, subclass Wagtail's FieldPanel, and then override the on_form_bound method. From here we can get a reference to the underlying Django ModelChoiceField, and now we can pass in our queryset (and also set empty_label to None if it's a required field).

from wagtail.admin.edit_handlers import FieldPanel

class MyFieldPanel(FieldPanel):

    def on_form_bound(self):
        choices = self.model.get_myfield_choices(self.model)
        self.form.fields['myfield'].queryset = choices
        self.form.fields['myfield'].empty_label = None

Overriding this method gives us access to the model the field is being called on - we can then pass this as a reference to our our queryset lookup.

def get_myfield_choices(self):
    return MyTaxonomy.objects.filter(page_types__contains=[self.related_page_type])