On loading, Wagtail will search for any app with the file wagtail_hooks.py and execute the contents. This provides a way to register your own functions to execute at certain points in Wagtail’s execution, such as when a page is saved or when the main menu is constructed.

Note

Hooks are typically used to customise the view-level behaviour of the Wagtail admin and front-end. For customisations that only deal with model-level behaviour - such as calling an external service when a page or document is added - it is often better to use Django’s signal mechanism (see also: Wagtail signals), as these are not dependent on a user taking a particular path through the admin interface.

Registering functions with a Wagtail hook is done through the @hooks.register decorator:

Alternatively, hooks.register can be called as an ordinary function, passing in the name of the hook and a handler function defined elsewhere:

hooks.register('name_of_hook',my_hook_function)

If you need your hooks to run in a particular order, you can pass the order parameter:

@hooks.register('name_of_hook',order=1)# This will run after every hook in the wagtail coredefmy_hook_function(arg1,arg2...)# your code here@hooks.register('name_of_hook',order=-1)# This will run before every hook in the wagtail coredefmy_other_hook_function(arg1,arg2...)# your code here@hooks.register('name_of_hook',order=2)# This will run after `my_hook_function`defyet_another_hook_function(arg1,arg2...)# your code here

Add or remove panels from the Wagtail admin homepage. The callable passed into this hook should take a request object and a list of panels, objects which have a render() method returning a string. The objects also have an order property, an integer used for ordering the panels. The default panels use integers between 100 and 300. Hook functions should modify the panels list in-place as required.

Add or remove items from the ‘site summary’ bar on the admin homepage (which shows the number of pages and other object that exist on the site). The callable passed into this hook should take a request object and a list of SummaryItem objects to be modified as required. These objects have a render() method, which returns an HTML string, and an order property, which is an integer that specifies the order in which the items will appear.

Called just before the Wagtail admin menu is output, to allow the list of menu items to be modified. The callable passed to this hook will receive a request object and a list of menu_items, and should modify menu_items in-place as required. Adding menu items should generally be done through the register_admin_menu_item hook instead - items added through construct_main_menu will be missing any associated JavaScript includes, and their is_shown check will not be applied.

Called when Wagtail needs to find out what objects exist in a collection, if any. Currently this happens on the confirmation before deleting a collection, to ensure that non-empty collections cannot be deleted. The callable passed to this hook will receive a collection object, and should return either None (to indicate no objects in this collection), or a dict containing the following keys:

count

A numeric count of items in this collection

count_text

A human-readable string describing the number of items in this collection, such as “3 documents”. (Sites with multi-language support should return a translatable string here, most likely using the django.utils.translation.ngettext function.)

Add an item to the Wagtail admin menu. The callable passed to this hook must return an instance of wagtail.admin.menu.MenuItem. New items can be constructed from the MenuItem class by passing in a label which will be the text in the menu item, and the URL of the admin page you want the menu item to link to (usually by calling reverse() on the admin view you’ve set up). Additionally, the following keyword arguments are accepted:

name:

an internal name used to identify the menu item; defaults to the slugified form of the label.

classnames:

additional classnames applied to the link, used to give it an icon

attrs:

additional HTML attributes to apply to the link

order:

an integer which determines the item’s position in the menu

For menu items that are only available to superusers, the subclass wagtail.admin.menu.AdminOnlyMenuItem can be used in place of MenuItem.

MenuItem can be further subclassed to customise the HTML output, specify JavaScript files required by the menu item, or conditionally show or hide the item for specific requests (for example, to apply permission checks); see the source code (wagtail/admin/menu.py) for details.

Register additional admin page URLs. The callable fed into this hook should return a list of Django URL patterns which define the structure of the pages and endpoints of your extension to the Wagtail admin. For more about vanilla Django URLconfs and views, see url dispatcher.

fromdjango.httpimportHttpResponsefromdjango.conf.urlsimporturlfromwagtail.coreimporthooksdefadmin_view(request):returnHttpResponse("I have approximate knowledge of many things!",content_type="text/plain")@hooks.register('register_admin_urls')defurlconf_time():return[url(r'^how_did_you_almost_know_my_name/$',admin_view,name='frank'),]

Add a new panel to the Groups form in the ‘settings’ area. The callable passed to this hook must return a ModelForm / ModelFormSet-like class, with a constructor that accepts a group object as its instance keyword argument, and which implements the methods save, is_valid, and as_admin_panel (which returns the HTML to be included on the group edit page).

Add an item to the Wagtail admin search “Other Searches”. Behaviour of this hook is similar to register_admin_menu_item. The callable passed to this hook must return an instance of wagtail.admin.search.SearchArea. New items can be constructed from the SearchArea class by passing the following parameters:

label:

text displayed in the “Other Searches” option box.

name:

an internal name used to identify the search option; defaults to the slugified form of the label.

url:

the URL of the target search page.

classnames:

additional CSS classnames applied to the link, used to give it an icon.

attrs:

additional HTML attributes to apply to the link.

order:

an integer which determines the item’s position in the list of options.

Setting the URL can be achieved using reverse() on the target search page. The GET parameter ‘q’ will be appended to the given URL.

A template tag, search_other is provided by the wagtailadmin_tags template module. This tag takes a single, optional parameter, current, which allows you to specify the name of the search option currently active. If the parameter is not given, the hook defaults to a reverse lookup of the page’s URL for comparison against the url parameter.

SearchArea can be subclassed to customise the HTML output, specify JavaScript files required by the option, or conditionally show or hide the item for specific requests (for example, to apply permission checks); see the source code (wagtail/admin/search.py) for details.

Rich text fields in Wagtail work with a list of ‘feature’ identifiers that determine which editing controls are available in the editor, and which elements are allowed in the output; for example, a rich text field defined as RichTextField(features=['h2','h3','bold','italic','link']) would allow headings, bold / italic formatting and links, but not (for example) bullet lists or images. The register_rich_text_features hook allows new feature identifiers to be defined - see Limiting features in a rich text field for details.

Do something with a Page object after it has been saved to the database (as a published page or a revision). The callable passed to this hook should take a request object and a page object. The function does not have to return anything, but if an object with a status_code property is returned, Wagtail will use it as a response object. By default, Wagtail will instead redirect to the Explorer page for the new page’s parent.

fromdjango.httpimportHttpResponsefromwagtail.coreimporthooks@hooks.register('after_create_page')defdo_after_page_create(request,page):returnHttpResponse("Congrats on making content!",content_type="text/plain")

Called at the beginning of the “create page” view passing in the request, the parent page and page model class.

The function does not have to return anything, but if an object with a status_code property is returned, Wagtail will use it as a response object and skip the rest of the view.

Unlike, after_create_page, this is run both for both GET and POST requests.

This can be used to completely override the editor on a per-view basis:

fromwagtail.coreimporthooksfrom.modelsimportAwesomePagefrom.admin_viewsimportedit_awesome_page@hooks.register('before_create_page')defbefore_create_page(request,parent_page,page_class):# Use a custom create view for the AwesomePage modelifpage_class==AwesomePage:returncreate_awesome_page(request,parent_page)

Add an item to the popup menu of actions on the page creation and edit views. The callable passed to this hook must return an instance of wagtail.admin.action_menu.ActionMenuItem. The following attributes and methods are available to be overridden on subclasses of ActionMenuItem:

order:

an integer (default 100) which determines the item’s position in the menu. Can also be passed as a keyword argument to the object constructor. The lowest-numbered item in this sequence will be selected as the default menu item; as standard, this is “Save draft” (which has an order of 0).

label:

the displayed text of the menu item

get_url:

a method which returns a URL for the menu item to link to; by default, returns None which causes the menu item to behave as a form submit button instead

name:

value of the name attribute of the submit button, if no URL is specified

is_shown:

a method which returns a boolean indicating whether the menu item should be shown; by default, true except when editing a locked page

template:

path to a template to render to produce the menu item HTML

get_context:

a method that returns a context dictionary to pass to the template

render_html:

a method that returns the menu item HTML; by default, renders template with the context returned from get_context

Media:

an inner class defining Javascript and CSS to import when this menu item is shown - see Django form media

The get_url, is_shown, get_context and render_html methods all accept a request object and a context dictionary containing the following fields:

view:

name of the current view: 'create', 'edit' or 'revisions_revert'

page:

For view = 'edit' or 'revisions_revert', the page being edited

parent_page:

For view = 'create', the parent page of the page being created

user_page_permissions:

a UserPagePermissionsProxy object for the current user, to test permissions against

Modify the final list of action menu items on the page creation and edit views. The callable passed to this hook receives a list of ActionMenuItem objects, a request object and a context dictionary as per register_page_action_menu_item, and should modify the list of menu items in-place.

The construct_page_action_menu hook is called after the menu items have been sorted by their order attributes, and so setting a menu item’s order will have no effect at this point. Instead, items can be reordered by changing their position in the list, with the first item being selected as the default action. For example, to change the default action to Publish:

@hooks.register('construct_page_action_menu')defmake_publish_default_action(menu_items,request,context):for(index,item)inenumerate(menu_items):ifitem.name=='action-publish':# move to top of listmenu_items.pop(index)menu_items.insert(0,item)break

Modify the final list of page listing buttons in the page explorer. The
callable passed to this hook receives a list of Button objects, a request
object and a context dictionary as per register_page_action_menu_item,
and should modify the list of menu items in-place.

@hooks.register('construct_page_listing_buttons')defremove_page_listing_button_item(buttons,page,page_perms,is_parent=False,context=None):ifis_parent:buttons.pop()# removes the last 'more' dropdown button on the parent page listing buttons

Add or remove items from the wagtail userbar. Add, edit, and moderation tools are provided by default. The callable passed into the hook must take the request object and a list of menu objects, items. The menu item objects must have a render method which can take a request object and return the HTML string representing the menu item. See the userbar templates and menu item classes for more information.

Do something with a User object after it has been saved to the database. The callable passed to this hook should take a request object and a user object. The function does not have to return anything, but if an object with a status_code property is returned, Wagtail will use it as a response object. By default, Wagtail will instead redirect to the User index page.

fromdjango.httpimportHttpResponsefromwagtail.coreimporthooks@hooks.register('after_create_user')defdo_after_page_create(request,user):returnHttpResponse("Congrats on creating a new user!",content_type="text/plain")

Called when rendering the page chooser view, to allow the page listing QuerySet to be customised. The callable passed into the hook will receive the current page QuerySet and the request object, and must return a Page QuerySet (either the original one, or a new one).

fromwagtail.coreimporthooks@hooks.register('construct_page_chooser_queryset')defshow_my_pages_only(pages,request):# Only show own pagespages=pages.filter(owner=request.user)returnpages

Called when rendering the document chooser view, to allow the document listing QuerySet to be customised. The callable passed into the hook will receive the current document QuerySet and the request object, and must return a Document QuerySet (either the original one, or a new one).

fromwagtail.coreimporthooks@hooks.register('construct_document_chooser_queryset')defshow_my_uploaded_documents_only(documents,request):# Only show uploaded documentsdocuments=documents.filter(uploaded_by_user=request.user)returndocuments

Called when rendering the image chooser view, to allow the image listing QuerySet to be customised. The callable passed into the hook will receive the current image QuerySet and the request object, and must return an Image QuerySet (either the original one, or a new one).

fromwagtail.coreimporthooks@hooks.register('construct_image_chooser_queryset')defshow_my_uploaded_images_only(images,request):# Only show uploaded imagesimages=images.filter(uploaded_by_user=request.user)returnimages

Called when rendering the page explorer view, to allow the page listing QuerySet to be customised. The callable passed into the hook will receive the parent page object, the current page QuerySet, and the request object, and must return a Page QuerySet (either the original one, or a new one).

fromwagtail.coreimporthooks@hooks.register('construct_explorer_page_queryset')defshow_my_profile_only(parent_page,pages,request):# If we're in the 'user-profiles' section, only show the user's own profileifparent_page.slug=='user-profiles':pages=pages.filter(owner=request.user)returnpages

Add buttons to the “More” dropdown menu for a page in the page explorer. This works similarly to the register_page_listing_buttons hook but is useful for lesser-used custom actions that are better suited for the dropdown.

The priority argument controls the order the buttons are displayed in the dropdown. Buttons are ordered from low to high priority, so a button with priority=10 will be displayed before a button with priority=60.

The admin widgets also provide ButtonWithDropdownFromHook, which allows you to define a custom hook for generating a dropdown menu that gets attached to your button.

Creating a button with a dropdown menu involves two steps. Firstly, you add your button to the register_page_listing_buttons hook, just like the example above.
Secondly, you register a new hook that yields the contents of the dropdown menu.

This example shows how Wagtail’s default admin dropdown is implemented. You can also see how to register buttons conditionally, in this case by evaluating the page_perms:

The template for the dropdown button can be customised by overriding wagtailadmin/pages/listing/_button_with_dropdown.html. The JavaScript that runs the dropdowns makes use of custom data attributes, so you should leave data-dropdown and data-dropdown-toggle in the markup if you customise it.

Called when Wagtail is about to serve a page. The callable passed into the hook will receive the page object, the request object, and the args and kwargs that will be passed to the page’s serve() method. If the callable returns an HttpResponse, that response will be returned immediately to the user, and Wagtail will not proceed to call serve() on the page.

Called when Wagtail is about to serve a document. The callable passed into the hook will receive the document object and the request object. If the callable returns an HttpResponse, that response will be returned immediately to the user, instead of serving the document. Note that this hook will be skipped if the WAGTAILDOCS_SERVE_METHOD setting is set to direct.

Called when a Snippet is edited. The callable passed into the hook will receive the model instance, the request object. If the callable returns an HttpResponse, that response will be returned immediately to the user, and Wagtail will not proceed to call redirect() to the listing view.

fromdjango.httpimportHttpResponsefromwagtail.coreimporthooks@hooks.register('after_edit_snippet')defafter_snippet_update(request,instance):returnHttpResponse(f"Congrats on editing a snippet with id {instance.pk}",content_type="text/plain")

Called when a Snippet is deleted. The callable passed into the hook will receive the model instance(s) as a queryset along with the request object. If the callable returns an HttpResponse, that response will be returned immediately to the user, and Wagtail will not proceed to call redirect() to the listing view.

fromdjango.httpimportHttpResponsefromwagtail.coreimporthooks@hooks.register('after_delete_snippet')defafter_snippet_delete(request,instances):# "instances" is a QuerySettotal=len(instances)returnHttpResponse(f"{total} snippets have been deleted",content_type="text/plain")