What is the correct way to extend Wagtail abstract models with extra fields?

对着背影说爱祢 提交于 2019-12-08 10:20:13

问题


I have an abstract Wagtail model with a few StreamFields. Two of these StreamFields are in a separate tab in the admin view, which are added to the edit_handler.

class AbstractHomePage(Page):
    body = StreamField(
        HomePageStreamBlock(),
        default=''
    )
    headingpanel = StreamField(
        HeadingPanelStreamBlock(),
        default=''
    )
    sidepanel = StreamField(
        SidePanelStreamBlock(),
        default=''
    )

    class Meta:
        abstract = True

    search_fields = Page.search_fields + [index.SearchField('body')]

    content_panels = Page.content_panels + [
        StreamFieldPanel('body'),
    ]

    pagesection_panels = [
        StreamFieldPanel('headingpanel'),
        StreamFieldPanel('sidepanel'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels),
        ObjectList(pagesection_panels, heading='Page sections'),
        ObjectList(Page.promote_panels),
        ObjectList(Page.settings_panels, classname='settings'),
    ])

I want to extend this model and add a field:

class Foo(AbstractHomePage):
    extra = models.TextField()

    Meta:
        verbose_name='Foo'

    content_panels = [
        AbstractHomePage.content_panels[0],     # title
        FieldPanel('extra'),
        AbstractHomePage.content_panels[-1]     # streamfield
    ]

When adding a new Foo page, the only fields available in the admin panel are the fields from the AbstractHomePage. The newly added field isn't available until I update Foo's edit_handler:

class Foo(AbstractHomePage):
    extra = models.TextField()

    Meta:
        verbose_name='Foo'

    content_panels = [
        AbstractHomePage.content_panels[0],     # title
        FieldPanel('extra'),
        AbstractHomePage.content_panels[-1]     # streamfield
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels),
        ObjectList(AbstractHomePage.pagesection_panels, heading='Page sections'),
        ObjectList(Page.promote_panels),
        ObjectList(Page.settings_panels, classname='settings'),
    ])

And now to my question: Have I done something wrong or not followed good coding practices?

If I do have to update the edit_handler for each extending model, is there a better way to do this? Having to ensure that newly added fields for models extending the AbstractHomePage gets the explicit "boilerplate" edit_handler block each time feels really ugly. I see it as a massive violation of the DRY principle.


回答1:


The reason you have to redefine edit_handler in Foo is that Python evaluates the AbstractHomePage class definition from top to bottom - at the point that it encounters the line:

ObjectList(content_panels),

content_panels is treated as a variable, not a class property, and so the edit handler is built based on the content_panels list as it existed at that point. Redefining content_panels in the subclass can't override this.

Essentially, you're looking for a way to defer constructing the edit_handler until the subclass has been defined. I can't see a good way of doing that directly, but I think you could achieve it with a bit of digging into Wagtail internals, by overriding the Page.get_edit_handler method:

from wagtail.utils.decorators import cached_classmethod

class AbstractHomePage(Page):
    ...
    @cached_classmethod
    def get_edit_handler(cls):
        edit_handler = TabbedInterface([
            ObjectList(cls.content_panels),
            ObjectList(cls.pagesection_panels, heading='Page sections'),
            ObjectList(cls.promote_panels),
            ObjectList(cls.settings_panels, classname='settings'),
        ])
        return edit_handler.bind_to_model(cls)


来源:https://stackoverflow.com/questions/41668167/what-is-the-correct-way-to-extend-wagtail-abstract-models-with-extra-fields

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!