[Python] Populating CheckboxSelectMultiple widget using my own model in Wagtail admin

Discussão em 'Python' iniciado por Stack, Novembro 27, 2024.

  1. Stack

    Stack Membro Participativo

    Context


    I've created a model, corresponding field model, and intend to reuse the built-in CheckboxSelectMultiple widget for use within the Wagtail admin. The concept is a multiple-select permission field that is saved as a bit-field:

    # Model class
    class Perm(IntFlag):
    Empty = 0
    Read = 1
    Write = 2


    I used Django's model field's documentation to create a field model that can translate my Perm type to and from my database (saved as an integer field that bitwise OR's the respective permission bits):

    # Model field class
    class PermField(models.Field):
    description = "Permission field"
    def __init__(self, value=Perm.Empty.value, *args, **kwargs):
    self.value = value
    kwargs["default"] = Perm.Empty.value
    super().__init__(*args, **kwargs)
    def deconstruct(self):
    name, path, args, kwargs = super().deconstruct()
    args += [self.value]
    return name, path, args, kwargs
    def db_type(self, connection):
    return "bigint" # PostgresSQL
    def from_db_value(self, value, expression, connection):
    if value is None:
    return Perm.Empty
    return Perm(value)
    def to_python(self, value):
    if isinstance(value, Perm):
    return value
    if isinstance(value, str):
    return self.parse(value)
    if value is None:
    return value
    return Perm(value)
    def parse(self, value):
    v = Perm.Empty
    if not isinstance(ast.literal_eval(value), list):
    raise ValueError("%s cannot be converted to %s", value, type(Perm))
    for n in ast.literal_eval(value):
    v = v | Perm(int(n))
    return v


    Then, I also created a Wagtail snippet to use this new field and type:

    perm_choices = [
    (Perm.Read.value, Perm.Read.name),
    (Perm.Write.value, Perm.Write.name)
    ]

    @register_snippet
    class Permission(models.Model):
    name = models.CharField(max_length=32, default="None")
    perm = PermField()
    panels = [FieldPanel("perm", widget=forms.CheckboxSelectMultiple(choices=perm_choices))]


    Problem


    Creating new snippets works fine, but editing an existing one simply shows an empty CheckboxSelectMultiple widget:

    [​IMG]

    Solution attempts


    I clearly need to populate the form when it's initialised. Ideally, making use of the built-in CheckboxSelectMultiple widget. To do that, I tried defining the following form:


    @register_snippet
    class Permission(models.Model):
    # ...

    # Custom form subclass for snippets per documentation
    # https://docs.wagtail.org/en/v2.15/advanced_topics/customisation/page_editing_interface.html
    class Permission(WagtailAdminModelForm):
    p = forms.IntegerField(
    widget=forms.CheckboxSelectMultiple(
    choices=perm_choices,
    ),
    label="Permission field",
    )
    def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.fields['p'].initial = {
    k.name for k in [Perm.Read, Perm.Write] if k.value in Perm(int(p))
    }

    def clean_selected_permissions(self):
    selected_p = self.cleaned_data["p"]
    value = Perm.Empty
    for k in selected_p:
    value |= Perm.__members__[k]
    return value

    class Meta:
    model=Permission
    fields=["perm"]

    # Models not defined yet error here!
    Permission.base_form_class = PermissionForm


    However, I cannot get this form to work. There's a cycle where PermissionForm requires Permission to be defined or vice-versa. Using a global model form assignment as seen here by gasman did not work for me. I'm also wondering if there's a simpler approach to solving the problem I'm facing that I'm just not seeing.

    Similar questions that didn't address my problem


    Continue reading...

Compartilhe esta Página