-
Notifications
You must be signed in to change notification settings - Fork 151
Description
Describe the bug
ClassifierSelectWidget always shows all classifiers and terms even if the underlying model field definition restricts choices using limit_choices_to. This allows users to click on invalid options causing the form to fail validation.
Steps to reproduce
Steps to reproduce the behavior:
- Set up a classifier with multiple terms. For this example I'm imagining a classifier called "Genre" with the terms "Action", "Adventure", "Horror", "Thriller", "Science-Fiction".
- Make a snippet model (new or existing) with the following field:
classifier_terms = ParentalManyToManyField(
'coderedcms.ClassifierTerm',
blank=True,
related_name="genres",
limit_choices_to=Q(name__startswith="A"), # or any filter eliminates some terms.
)- In the model's
panelsattribute, includeFieldPanel("classifier_terms", widget=ClassifierSelectWidget()). - Run migrations, register snippet, etc.
- Go to the snippet edit page for the snippet.
- Scroll to the "classifier terms" panel. See that all of the terms are present, not just ones matching the
Qobject. - Select both a matching term and a non-matching term, and save the form. The form validation should fail and the error message should complain only about the ID of the non-matching term.
Expected behavior
Only the terms and classifiers matching the limit_choices_to filter should be available as choices, as happens when not using the ClassifierSelectWidget(). CheckboxSelectMultiple is very close to what I want, but it is not grouped by classifier.
Additional context
This should theoretically be a simple matter of updating the optgroups class to use self.choices.queryset.
I made a simple working example by changing the initial classifier queryset from Classifier.objects.all().select_related() to
classifiers = Classifier.objects.all().select_related().prefetch_related(
Prefetch("terms", queryset=self.choices.queryset)
)to stop Classifiers with no matching terms from appearing as empty groupings, I just changed the inner loop to this:
if terms := classifier.terms.all():
for term in terms:
If this seems like something y'all would be interested in implementing, I could try my hand at making a merge request. Wanted to confirm that the team would consider it worthwhile.