Last active
December 31, 2020 15:14
-
-
Save kristinriebe/0d428bde46cf900445a982dfa9ca7857 to your computer and use it in GitHub Desktop.
Wagtail modelcluster with modelchooser: Example for ModelChooserPanel with filter_name for limiting choices
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
from modelcluster.fields import ParentalKey | |
from modelcluster.models import ClusterableModel | |
from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel | |
from wagtail.core.models import Orderable | |
from wagtail.search import index | |
from wagtailmodelchooser import register_filter | |
from wagtailmodelchooser.edit_handlers import ModelChooserPanel | |
""" | |
Example for filtering the choices for a foreign key model, | |
which is included in the parent model's admin panels as InlinePanel | |
and selection happens via ModelChooserPanel from wagtailmodelchooser. | |
There is a 'limit_choices_to' keyword to ForeignKey that nearly | |
does what I want, but in combination with ModelChooserPanel it | |
only checks the choices during validation of the admin forms, | |
the selection list is still the full list and not limited. | |
""" | |
class Section(index.Indexed, models.Model): | |
# A simplified Section model | |
name = models.CharField(max_length=200) | |
useful_for_thesis = models.BooleanField(null=True, default=True) | |
search_fields = [ | |
index.FilterField('name', partial_match=True) | |
] | |
class Thesis(ClusterableModel): | |
# A simplified Thesis model; a thesis can be assigned to many sections, | |
# and sections can be assigned to many thesis objects, therefore | |
# it is a many-to-many relationship and we'll define a corresponding | |
# link with the class below. | |
# Include the sections here as InlinePanel, so the sections can be defined | |
# with each new thesis. | |
title = models.CharField(max_length=200) | |
panels = [ | |
FieldPanel('title'), | |
MultiFieldPanel([ InlinePanel('sections'), ], heading="Sections"), | |
] | |
class ThesisSectionLink(Orderable): | |
thesis = ParentalKey(Thesis, related_name='sections') # related_name here must match the InlinePanel-name above | |
section = models.ForeignKey(Section, on_delete=models.SET_NULL, related_name="+", null=True) | |
# When a new Thesis object is created or edited, then one or more | |
# Section objects shall be added. | |
# If no additional filter shall be applied and all sections shall be listed, | |
# then use a simple `panels = [ ModelChooserPanel('section'), ]`. | |
# However, we want to limit the sections using a filter function. wagtailmodelchooser | |
# allows to define a filter, whose name can be added to ModelChooserPanel using | |
# `filter_name`. | |
panels = [ | |
ModelChooserPanel('section', filter_name='thesis_sections_filter') | |
] | |
# Define the filter; use here a very simple additional filter for illustration purposes, | |
# but it could also be a more complicated function here, as long as it takes a queryset | |
# as input and returns the modified queryset. | |
@register_filter(Section, 'thesis_sections_filter') | |
def get_useful_sections(qs): | |
# Attention: qs.filter() only works for models that are not searchable! | |
# Modelchooser gives a nice search field in admin, if Section is searchable, e.g. it | |
# inherits from index.Indexed. | |
# If the user puts a search string in the search field in the modelchooser popup, then | |
# first the search query is executed, then the filter function is applied. | |
# Therefore, qs.filter() would return an error: Elasticsearch5SearchResults object has no attribute | |
# 'filter'. | |
# Thus, we check this here and apply manual filtering, if needed. | |
if hasattr(qs, 'filter'): | |
return qs.filter(useful_for_thesis=True) | |
else: | |
new_qs = [] | |
for section in qs: | |
if section.useful_for_thesis: | |
new_qs.append(qs) | |
return new_qs | |
# or in one line: | |
# return [section for section in qs if section.useful_for_thesis] | |
# If not using the decorator above, one can also use the filter function directly: | |
# register_filter(Section, 'thesis_sections_filter', get_useful_sections) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment