Show different panels in wagtail admin snippets create and edit

211 views Asked by At

I have a model in wagtail with 3 fields

class MyModel(models.Model):
    name = models.CharField(_("Name"), max_length=50, unique=True)
    tag = models.CharField(_("Tag"), max_length=10, unique=True)
    is_valid = models.BooleanField(default=False)

the tag field cannot be changed in CreateView but only in EditView.

So when I click on Add i want this panels

create_panels = [
    FieldPanel("name"),
    FieldPanel("tag"),
    FieldPanel("is_valid"),
]

instead if I click on Edit these are the panels

edit_panels = [
    FieldPanel("name"),
    FieldPanel("tag", readonly=True),
    FieldPanel("is_valid"),
]

With previous versions of Wagtail I was able to perform this using ModelAdmin, but now I am using Wagtail 5.1.1 and if I use ModelAdmin it tells me it is deprecated and to use Snippets.

So I created a SnippetViewSet with a custom EditView and CreateView like this:

class EditableEditView(EditView):
    def get_panel(self):
        panel = ObjectList(edit_panels)
        return panel.bind_to_model(self.object.__class__)


class EditableCreateView(CreateView):
    def get_panel(self):
        panel = ObjectList(create_panels)
        return panel.bind_to_model(self.object.__class__)


class EditableSnippetViewSet(SnippetViewSet):
    model = MyModel

    edit_view_class = EditableEditView
    create_view_class = EditableCreateView
    ...

The result is that in the EditView the tag field is not readonly and it's duplicated at the end of the page like this. Digging into the code i see that the duplicated field comes from the BoundedPanel.render_form_content, to be precise it comes when it's called self.render_missing_fields(). I can't understand why the tag fields is considered a missing field and why the readonly has no effect.

2

There are 2 answers

0
Capa00 On

I found a solution that seems to work for wagtail 5.1.1. First I create two different view:

class EditableAddView(CreateView):
    def get_form(self, form_class=None):
        custom_form_class = self.panel.get_form_class()
        return super().get_form(form_class=custom_form_class)


class EditableEditView(EditView):
    def get_form(self, form_class=None):
        custom_form_class = self.panel.get_form_class()
        return super().get_form(form_class=custom_form_class)

and the a custom snippet view set

class EditableSnippetViewSet(SnippetViewSet):
    add_view_class = EditableAddView
    edit_view_class = EditableEditView
    create_panels = None
    edit_panels = None
    create_edit_handler = None
    edit_edit_handler = None

    def get_create_edit_handler(self):
        if self.create_edit_handler is not None:
            handler = self.create_edit_handler
        else:
            handler = ObjectList(self.create_panels or self.panels)
        self.create_edit_handler = handler.bind_to_model(self.model)
        return self.create_edit_handler

    def get_edit_edit_handler(self):
        if self.edit_edit_handler is not None:
            handler = self.edit_edit_handler
        else:
            handler = ObjectList(self.edit_panels)
        self.edit_edit_handler = handler.bind_to_model(self.model)
        return self.edit_edit_handler

    @property
    def add_view(self):
        return self.add_view_class.as_view(
            model=self.model,
            template_name=self.get_create_template(),
            header_icon=self.icon,
            permission_policy=self.permission_policy,
            panel=self.get_create_edit_handler(),
            form_class=self.get_form_class(),
            index_url_name=self.get_url_name("list"),
            add_url_name=self.get_url_name("add"),
            edit_url_name=self.get_url_name("edit"),
            preview_url_name=self.get_url_name("preview_on_add"),
        )

    @property
    def edit_view(self):
        # Any parameters passed here must also be passed in revisions_revert_view.
        return self.edit_view_class.as_view(
            model=self.model,
            template_name=self.get_edit_template(),
            header_icon=self.icon,
            permission_policy=self.permission_policy,
            panel=self.get_edit_edit_handler(),
            form_class=self.get_form_class(for_update=True),
            index_url_name=self.get_url_name("list"),
            edit_url_name=self.get_url_name("edit"),
            delete_url_name=self.get_url_name("delete"),
            history_url_name=self.get_url_name("history"),
            preview_url_name=self.get_url_name("preview_on_edit"),
            lock_url_name=self.get_url_name("lock"),
            unlock_url_name=self.get_url_name("unlock"),
            usage_url_name=self.get_url_name("usage"),
            revisions_compare_url_name=self.get_url_name("revisions_compare"),
            revisions_unschedule_url_name=self.get_url_name("revisions_unschedule"),
            workflow_history_url_name=self.get_url_name("workflow_history"),
            confirm_workflow_cancellation_url_name=self.get_url_name(
                "confirm_workflow_cancellation"
            ),
        )

    @property
    def revisions_revert_view(self):
        return self.revisions_revert_view_class.as_view(
            model=self.model,
            template_name=self.get_edit_template(),
            header_icon=self.icon,
            permission_policy=self.permission_policy,
            panel=self.get_edit_edit_handler(),
            form_class=self.get_form_class(for_update=True),
            index_url_name=self.get_url_name("list"),
            edit_url_name=self.get_url_name("edit"),
            delete_url_name=self.get_url_name("delete"),
            history_url_name=self.get_url_name("history"),
            preview_url_name=self.get_url_name("preview_on_edit"),
            lock_url_name=self.get_url_name("lock"),
            unlock_url_name=self.get_url_name("unlock"),
            usage_url_name=self.get_url_name("usage"),
            revisions_compare_url_name=self.get_url_name("revisions_compare"),
            revisions_unschedule_url_name=self.get_url_name("revisions_unschedule"),
            revisions_revert_url_name=self.get_url_name("revisions_revert"),
            workflow_history_url_name=self.get_url_name("workflow_history"),
            confirm_workflow_cancellation_url_name=self.get_url_name(
                "confirm_workflow_cancellation"
            ),
        )

The panel of the edit and create view are based on the create_panels and the edit_panels of the EditableSnippetViewset.

So now i can create my snippet view set like this.

class MySnippetViewset(EditableSnippetViewSet):
    ...
    
    edit_panels = [
        FieldPanel("name"),
        FieldPanel("tag"),
        FieldPanel("is_valid"),
    ]

    create_panels = [
        FieldPanel("name"),
    ]
0
Eduardo C. On

I had a different approach:

class WagtailArticleAdmin(SnippetViewSet):
    model = Article
    icon = "openquote"
    menu_order = 393
    add_to_settings_menu = True
    exclude_from_explorer = False
    list_display = ("title", "created_at", "updated_at")
    search_fields = ("title",)

    create_panels = [
        MultiFieldPanel(
            [
                FieldPanel("title"),
            ],
            heading="Artigo",
        ),
    ]

    edit_panels = [
        MultiFieldPanel(
            [
                FieldPanel("title"),
                FieldPanel("content"),
            ],
            heading="Artigo",
        ),
    ]

    add_edit_handler = TabbedInterface(
        [
            ObjectList(create_panels, heading="Artigo"),
        ]
    )

    edit_edit_handler = TabbedInterface(
        [
            ObjectList(edit_panels, heading="Artigo"),
        ]
    )

    def edit_view_edit_handler(self):
        return self.edit_edit_handler.bind_to_model(self.model)

    def add_view_edit_handler(self):
        return self.add_edit_handler.bind_to_model(self.model)

    def get_add_view_kwargs(self, **kwargs):
        edit_handler = self.add_view_edit_handler()
        return {
            "panel": edit_handler,
            "form_class": edit_handler.get_form_class(),
            "template_name": self.create_template_name,
            **kwargs,
        }

    def get_edit_view_kwargs(self, **kwargs):
        edit_handler = self.edit_view_edit_handler()
        return {
            "panel": edit_handler,
            "form_class": edit_handler.get_form_class(),
            "template_name": self.edit_template_name,
            **kwargs,
        }