Migrating Wagtail ModelAdmin to the new snippet admin views

Header image

With the new snippet admin views the usage of modeladmin becomes outdated. The snippets now leverage the preview & history abilities from Wagtail, similar to the workflow of pages:

  • Preview
  • Draft & publish workflow
  • Search
  • Filters

However we find that it's most important to lift upon the core features of the Wagtail CMS so we can translate these snippets as well in the future.

Let's see how we can migrate from our existing modeladmin setup and how it looks.

First of all we have the current modeladmin configuration for a common used Person model:

class Person(models.Model): first_name = models.CharField(_("First name"), max_length=255) last_name = models.CharField(_("Last name"), max_length=255) function = models.CharField(_("Function"), max_length=255) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=False, on_delete=models.SET_NULL, related_name="+", verbose_name=_("image"), ) panels = [ MultiFieldPanel( [ FieldPanel("first_name"), FieldPanel("last_name"), FieldPanel("function"), FieldPanel("image"), ], heading=_("person"), ), ] class Meta: ordering = ["first_name"] verbose_name = _("person") verbose_name_plural = _("persons") def __str__(self): return "%s %s" % (self.first_name, self.last_name) class PersonModelAdmin(ModelAdmin): model = Person list_display = ("first_name", "last_name", "function") search_fields = ("first_name", "last_name", "function")

In order to provide our existing model with the latest snippet features we have to extend this model quite a bit:

class Person( index.Indexed, PreviewableMixin, DraftStateMixin, RevisionMixin, models.Model ): first_name = models.CharField(_("First name"), max_length=255) last_name = models.CharField(_("Last name"), max_length=255) function = models.CharField(_("Function"), max_length=255) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=False, on_delete=models.SET_NULL, related_name="+", verbose_name=_("image"), ) _revisions = GenericRelation("wagtailcore.Revision", related_query_name="person") search_fields = [ index.SearchField("first_name", partial_match=True, boost=10), index.SearchField("last_name", partial_match=True, boost=10), index.SearchField("function", partial_match=True), ] panels = [ MultiFieldPanel( [ FieldPanel("first_name"), FieldPanel("last_name"), FieldPanel("function"), FieldPanel("image"), ], heading=_("person"), ), PublishingPanel(), ] class Meta: ordering = ["first_name"] verbose_name = _("person") verbose_name_plural = _("persons") def __str__(self): return "%s %s" % (self.first_name, self.last_name) def get_preview_template(self, request, mode_name): return "cms/previews/person.html"

With the new SnippetViewSet we create our configuration for our Person model (for now we leave the option to add filters for as it is).

from wagtail.snippets.views.snippets import SnippetViewSet class PersonViewSet(SnippetViewSet): list_display = ["first_name", "last_name"]

Finally register the snippet with this PersonViewSet (make sure this is done in wagtail_hooks.py as you'll get errors about models which are not loaded yet):

register_snippet(Person, viewset=PersonViewSet)