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