0

In a wagtail settings model I have a CharField-based choice field and want that to behave as a multi select in Wagtails FieldPanel. The choices are rendered correctly as multiple checkboxes, but on submit the form validation will raise an error:

Select a valid choice. ['OPT1'] is not one of the available choices.

So how do I use text-based choices as a multi select in Wagtails FieldPanel?
Do I have to override the form field type to a forms.fields.MultipleChoiceField somehow?

Setting the widget attribute to Select - instead of CheckboxSelectMultiple - the correctly rendered and populated select dropdown widget works as expected.

My current implementation looks (roughly) like this:

# models.py

@register_setting(icon='cog')
class MySettings(BaseGenericSetting):

    class MyChoices(models.TextChoices):
        OPT_1 = "OPT1"
        OPT_2 = "OPT2"

    choicefield = models.CharField(
        max_length=4,
        blank=True,
        choices=MyChoices.choices,
    )

    panels = [
        FieldPanel(
            'choicefield',
            widget=forms.CheckboxSelectMultiple,
        ),
    ]

I did not find any hints in the Customising generated forms docs.

tombreit
  • 1,199
  • 8
  • 27

1 Answers1

0

This'll be because CheckboxSelectMultiple is returning an array of selected items but the validator is expecting a single value found in MyChoices.

I think this is what you need:

class MyChoices(models.Model):
    item = models.CharField(max_length=255)

@register_setting(icon='cog')
class MySettings(BaseGenericSetting):
    choicefield = models.ManyToManyField(MyChoices)
    panels = [
        FieldPanel(
            'choicefield',
            widget=forms.CheckboxSelectMultiple,
        ),
    ]

You'd need to populate MyChoices from py command line or in the db admin, or register it as a snippet and add your values in the Wagtail admin interface.

Also, take a look at https://docs.wagtail.org/en/stable/getting_started/tutorial.html#tutorial-categories which creates a multi-multi choice field for blog categories using a ParentalManyToManyField. This is how I handle this scenario in Wagtail.

In the background, it sets up an intermediate table holding pairs of id's from each model to create the link.

  • Thanks, but I think that works for a `ManyToManyField` which points to a related model. But I do not want to have my choices in a separate model/table, but hardcoded as eg. `TextChoices`. In the case of a `ManyToManyField` wagtail automatically gets the correct form field class. – tombreit Jun 14 '23 at 08:05
  • Perhaps I have to override the form and set the field type to `forms.ModelMultipleChoiceField` - but I have now clue howto set a custom form class for a `BaseGenericSetting`... – tombreit Jun 14 '23 at 09:18
  • Wagtail/Django defaults m2m with a select panel which pretty nasty to use. The problem you have is trying to store multiple values in a CharField. You'd need to serialise that some how and split it back when rendering. Maybe this is useful for you? https://pypi.org/project/django-multiselectfield/ – Rich - enzedonline Jun 14 '23 at 11:04
  • Ok, then I had imagined it too simple/too easy. The django-multiselectfield perhaps would fit, but does not seem to be maintained anymore. And unless there is a "django-batteries-included" solution to represent choices (from constants, not related models) as multiselect, I drop this idea. I've found some hints in the [wagtail codebase](https://github.com/wagtail/wagtail/blob/fd83bedd723badc20f7966c0332fa7bfc83b5227/docs/extending/customising_group_views.md?plain=1#L43), but could not translate them for my use case. Thanks a lot for your contribution! – tombreit Jun 15 '23 at 09:08