How to Add Django Models to the Wagtail Admin

williln

Lacey Williams Henschel

Posted on August 27, 2019

How to Add Django Models to the Wagtail Admin

Versions

  • Python 3.7
  • Django 2.2
  • Wagtail 2.6

When working with Wagtail, you might find that you're using Wagtail Page models for some of your database models, but regular Django models for others.

A built-in example of this is the Django User model. When you log into the Wagtail admin, you can see the Django User model in the Settings submenu. The User model is not a Wagtail model; it's the same User model you see in a Django project that doesn't use Wagtail. Wagtail just exposes it to the Admin for you.

Users menu under the Settings menu in the Wagtail admin

We can do the same thing with our Django models: we can expose them to the Wagtail admin so we don't have to maintain two separate admin interfaces to manage our website content.

For this example, let's assume we're working with these models:

from django.db import models 


class Pizza(models.Model):

    name = models.CharField(max_length=30)
    toppings = models.ManyToManyField("Topping")


class Topping(models.Model):

    name = models.CharField(max_length=30)
Enter fullscreen mode Exit fullscreen mode

Adding a single model

The Wagtail docs are pretty clear on how to accomplish this, but let's walk through the steps.

First, make sure wagtail.contrib.modeladmin is in your INSTALLED_APPS:

# settings.py 

INSTALLED_APPS = [
    ...
    "wagtail.contrib.modeladmin",
]
Enter fullscreen mode Exit fullscreen mode

Next, in the same app as the model you want to expose to the Wagtail admin, add a file called wagtail_hooks.py.

# wagtail_hooks.py

from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 

from .models import Pizza


class PizzaAdmin(ModelAdmin):
    model = Pizza 
    menu_label = "Pizza"  
    menu_icon = "pick" 
    menu_order = 200 
    add_to_settings_menu = False 
    exclude_from_explorer = False 
    list_display = ("name",)
    list_filter = ("toppings",)
    search_fields = ("name",)


modeladmin_register(PizzaAdmin)
Enter fullscreen mode Exit fullscreen mode

Let's step through these options in the ModelAdmin class:

  • model: The name of the model you're adding.
  • menu_label: Leave this blank to use the verbose_name_plural from your model. Give it a value to specify a new label for the Wagtail menu.
  • menu_icon: Every menu item in the Wagtail admin has an icon, and you can specify the one you want to use. Here is a list of the available icons.
  • menu_order: What order you want this model to appear in. 000 is first, 100 is second, etc. Note: if you add multiple models to the admin, you won't get an error if two of them have the same menu_order; Wagtail will just pick for you.
  • add_to_settings_menu: Whether you want this menu item to appear in the Settings submenu in the Wagtail admin.
  • exclude_from_explorer: Set to True if you do not want the explorer (the search box in the admin) to return results from this model. Set to False if you do want the explorer to return results from this model. (It's confusing.)
  • list_display: Same as the Django admin; list the fields you want to display on the listing page for this model in the Wagtail admin.
  • list_filter: Same as the Django admin; supply the fields you want to use to filter in the sidebar of the Wagtail admin.
  • search_fields: Same as the Django admin; supply the fields that you want the explorer to use to return search results.

The final step is to register the admin class. Once you've done that and started your server, you'll be able to see your model in the Wagtail admin:

We can do the same thing with our Django models: we can expose them to the Wagtail admin so we don't have to maintain two separate admin interfaces to manage our website content.

For this example, let's assume we're working with these models:

from django.db import models 


class Pizza(models.Model):

    name = models.CharField(max_length=30)
    toppings = models.ManyToManyField("Topping")


class Topping(models.Model):

    name = models.CharField(max_length=30)
Enter fullscreen mode Exit fullscreen mode

Adding a single model

The Wagtail docs are pretty clear on how to accomplish this, but let's walk through the steps.

First, make sure wagtail.contrib.modeladmin is in your INSTALLED_APPS:

# settings.py 

INSTALLED_APPS = [
    ...
    "wagtail.contrib.modeladmin",
]
Enter fullscreen mode Exit fullscreen mode

Next, in the same app as the model you want to expose to the Wagtail admin, add a file called wagtail_hooks.py.

# wagtail_hooks.py

from wagtail.contrib.modeladmin.options import ModelAdmin, modeladmin_register 

from .models import Pizza


class PizzaAdmin(ModelAdmin):
    model = Pizza 
    menu_label = "Pizza"  
    menu_icon = "pick" 
    menu_order = 200 
    add_to_settings_menu = False 
    exclude_from_explorer = False 
    list_display = ("name",)
    list_filter = ("toppings",)
    search_fields = ("name",)


modeladmin_register(PizzaAdmin)
Enter fullscreen mode Exit fullscreen mode

Let's step through these options in the ModelAdmin class:

  • model: The name of the model you're adding.
  • menu_label: Leave this blank to use the verbose_name_plural from your model. Give it a value to specify a new label for the Wagtail menu.
  • menu_icon: Every menu item in the Wagtail admin has an icon, and you can specify the one you want to use. Here is a list of the available icons.
  • menu_order: What order you want this model to appear in. 000 is first, 100 is second, etc. Note: if you add multiple models to the admin, you won't get an error if two of them have the same menu_order; Wagtail will just pick for you.
  • add_to_settings_menu: Whether you want this menu item to appear in the Settings submenu in the Wagtail admin.
  • exclude_from_explorer: Set to True if you do not want the explorer (the search box in the admin) to return results from this model. Set to False if you do want the explorer to return results from this model. (It's confusing.)
  • list_display: Same as the Django admin; list the fields you want to display on the listing page for this model in the Wagtail admin.
  • list_filter: Same as the Django admin; supply the fields you want to use to filter in the sidebar of the Wagtail admin.
  • search_fields: Same as the Django admin; supply the fields that you want the explorer to use to return search results.

The final step is to register the admin class. Once you've done that and started your server, you'll be able to see your model in the Wagtail admin:

Pizzas menu in sidebar in the Wagtail admin

Adding related models

In our example models, we have two models: Pizza and Toppings. We could manually add the Topping model to the Wagtail admin and have it appear just below the Pizza model. We just learned how!

But it's so closely related to the Pizza model that it might be nice if we were able to relate those two models together in a submenu, kind of like how Settings is its own submenu in the admin that contains Users, Redirects, Sites, etc.

Go back to wagtail_hooks.py:

# wagtail_hooks.py

from wagtail.contrib.modeladmin.options import (
    ModelAdmin, 
    ModelAdminGroup, 
    modeladmin_register 
)

from .models import Pizza, Topping


class PizzaAdmin(ModelAdmin):
    ...
    menu_order = 000 
    ...


class ToppingAdmin(ModelAdmin):
    model = Topping 
    menu_label = "Toppings"  
    menu_icon = "edit" 
    menu_order = 100 
    add_to_settings_menu = False 
    exclude_from_explorer = False 
    list_display = ("name",)
    search_fields = ("name",)
Enter fullscreen mode Exit fullscreen mode

Relating our two models together starts off in the same way: we create a class that inherits from ModelAdmin for each model and identify the necessary attributes like model and menu_icon to control things like their listing pages and search behavior.

Then, we add a new class that inherits from ModelAdminGroup:

# wagtail_hooks.py

from wagtail.contrib.modeladmin.options import (
    ModelAdmin, 
    ModelAdminGroup, 
    modeladmin_register 
)

from .models import Pizza, Topping


class PizzaAdmin(ModelAdmin):
    ...
    menu_order = 000 
    ...


class ToppingAdmin(ModelAdmin):
    ...
    menu_order = 100 
    ... 

class PizzaGroup(ModelAdminGroup):
    menu_label = "Pizzas" 
    menu_icon = "pick"
    menu_order = 500 
    items = (PizzaAdmin, ToppingAdmin)


modeladmin_register(PizzaGroup)
Enter fullscreen mode Exit fullscreen mode

In the PizzaGroup class, we have some of the same attributes:

  • menu_label: We set what we want this group of related models to be called in the Wagtail admin menu
  • menu_icon: Which icon we want to use for this menu
  • menu_order: Where we want this menu to appear in the sidebar, in relation to the other menu items

We also add a new attribute, items, where we list which ModelAdmin classes we want to be part of this group. In our case, we want PizzaAdmin and ToppingAdmin to be in this group, so we add those.

Note the change we made to menu_order in PizzaAdmin and ToppingAdmin: Now those are set to 000 and 100. When the ModelAdmin classes will be part of a group, set the menu_order how you want them to relate to each other, not to the other menu items in the Wagtail admin. Then set the menu_order for the ModelAdminGroup class to the proper value for the order you want it to appear in the side menu in the admin.

Then we register the whole group, instead of the ModelAdmin classes individually, to the Wagtail admin. When we reload the admin, we see this:

Pizzas menu in the Wagtail admin that opens to reveal a Pizza and Toppings menu

On the far left, there is a new menu item Pizzas that expands a submenu. The submenu contains links to the admin interfaces for Pizzas and Toppings!

Note: If you have the Django admin enabled and have your models already in the Django admin, this doesn't disable them from the regular Django admin. You are free to access your models in both the Wagtail admin and the Django admin, or at this point you can choose to remove your models from the Django admin (or disable the Django admin altogether, if you prefer).

Helpful Links

Special thanks to Jeff Triplett and Jacob Burch for their help with this post.

💖 💪 🙅 🚩
williln
Lacey Williams Henschel

Posted on August 27, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related