Cheluis
Cheluis

Reputation: 1412

Get parent page on creating new Wagtail Page

I need to change some field default value given a value from the parent page when creating a new page. When editing an existing page, this is not a problem, but when creating a new page, I need to set a default value for a field. I have tried overriding the admin form, on init for WagtailAdminPageForm, the parent_page parameter is empty. And if I try to do it on the init method form Page subclass, there is no arg or kwargs with the parent information.

is there a way to get the page for which the new page will be the parent?

This is what I tried on Page constructor

class CMSPage(Page):
    .
    .
    .
    def __init__(self, *args, **kwargs):
        super(BaseContentMixin, self).__init__(*args, **kwargs)

        if hasattr(self.get_parent().specific, 'published_site') and self.get_parent().specific.published_site == 'extranet':
            self.published_site = 'extranet'

This works editing a page, for new page, I get that NoneType objects has no attribute specific.

Django version is 1.10, Python version is 3.6, Wagtail version is 1.9

Upvotes: 5

Views: 5818

Answers (2)

LB Ben Johnston
LB Ben Johnston

Reputation: 5176

After clarification of the question, here is a second answer that might be more suitable. Note that this requires Wagtail 1.11.x which as at this time is not yet released.

Example Code for Solution

First Create a new class for your custom page form, usually in models.py or wherever the model for your page is.

from wagtail.wagtailadmin.forms import WagtailAdminPageForm

class MyCustomPageForm(WagtailAdminPageForm):

    # Override the __init__ function to update 'initial' form values
    def __init__(self, data=None, files=None, parent_page=None, *args, **kwargs):
        print('parent_page', parent_page, parent_page.id, parent_page.title)
        # update the kwargs BEFORE the init of the super form class
        instance = kwargs.get('instance')
        if not instance.id:
            # only update the initial value when creating a new page
            kwargs.update(initial={
                # 'field': 'value'
                'title': parent_page.id  # can be anything from the parent page
            })
        # Ensure you call the super class __init__
        super(MyCustomPageForm, self).__init__(data, files, *args, **kwargs)
        self.parent_page = parent_page

Second On the page model definition, tell that model to use the form class you have defined

from wagtail.wagtailcore.models import Page

class MyCustomPage(Page):
    base_form_class = MyCustomPageForm

Explanation of Solution

  • Wagtail provides the ability to override the form class (note: not model class) that gets called when building the form for creation and editing in Wagtail Admin.
  • Documentation: Customising generated forms
  • Our custom form class will inherit the WagtailAdminPageForm class.
  • Inside this new form class, we want to define our own __init__ function, this is called when the form is created.
  • Note the Github code for the default definition of __init__ on WagtailAdminPageForm
  • To inject custom values, we will override the initial data in kwargs before the calling of the class' super __init__
  • We only want to do this when a new page is being created, so check that the instance has no id
  • We can access the parent_page instance and any fields in it
  • After adding our custom initial data, then we call the super __init__ with the changed kwargs
  • Note: The reason for the requirement of Wagtail 1.11.x is that previous versions until that point did not add parent_page when calling the class model until pull request 3508

Upvotes: 4

LB Ben Johnston
LB Ben Johnston

Reputation: 5176

I would recommend using the Wagtail Hooks:

http://docs.wagtail.io/en/v1.10.1/reference/hooks.html

Here is a basic example that works (in the Wagtaildemo app), in this example, I am just getting the parent page's title. It should work the same if you are getting something else from the parent's page specific model. Note that this updates the record after it has been created, not as part of the creation itself.

# my_app/wagtail_hooks.py
from wagtail.wagtailcore.models import Page
from wagtail.wagtailcore import hooks


@hooks.register('after_create_page')
def do_after_page_create(request, page):
    # get the parent page from the current page
    parent = page.get_parent().specific
    # get the value from the parent page
    parent_title = parent.title
    # get a new instance of the page that was just created
    new_page = Page.objects.get(id=page.id).specific
    # update the value in the page that was just created & save
    new_page.parent_title = parent_title
    new_page.save()

I have used the Wagtail hooks a lot and they work well, you could further refine this by checking the new_page.content_type == "CMSPage" to ensure this only updates pages of that specific model.

Finally, it might be good to look into how Wagtail sites works, this might be a more specific solution to your issue in the example code. You can add extra settings to your Site Model and have multiple Sites in Wagtail. I am not sure if your example was just a rough example or the exact issue you are facing.

Upvotes: 1

Related Questions