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
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 super().on_form_bound()
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
def get_myfield_choices(self): return MyTaxonomy.objects.filter(page_types__contains=[self.related_page_type])