问题
I have an abstract Wagtail model with a few StreamField
s. Two of these StreamField
s 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