There are trade-offs with everything (or how I stopped worrying and learned to love Wagtail CMS)
Wagtail is a CMS framework built on top of Django that takes away some of tedium of creating a CMS from scratch. It has a nice extendable interface for editors to interact with, page reversion tracking, a spiffy admin UI, easy uploading of images, and lots of other goodies.
I'll be honest -- coming from Django where I have a pretty good grasp of how things work (and where to figure things out if I need to) was a hard transition to Wagtail. It definitely took a little time to understand its philosophy around content.
But, at this point, I have come to appreciate all of the benefits that Wagtail provides... and work around some of the pieces that I like less. One thing that took me a while to understand is that Wagtail uses the term snippet to refer to Django models that aren't explicitly a Page. Coming from Django, I am used to defining a data model and tying models together with many-to-many or foreign key relationships. Snippets are where to store those pieces of information that could be shared with many different Page models. However, snippets feel like they are not completely integrated into the Wagtail experience, and they lose a lot of the nice functionality that comes "for free" with a page model -- one glaring omission is the lack of revisions for snippets. There are a few open issues for this in Wagtail (notably https://github.com/wagtail/wagtail/issues/4541 and https://github.com/wagtail/wagtail/issues/2270).
Django audits in one easy payment of $0
One package that I have really appreciated in the past with a normal Django application is django-reversion which integrates tightly into the Django admin and automatically saves versions of an object as an audit trail. Wagtail provides something very similar for Page models, but snippets eren't invited to the party and feel very left out.
ยกAy, caramba! Automatic auditing for Wagtail snippets!
All is well in Wagtail-land, though! With a little perserverance, Wagtail snippets can also get automatic revisions and it isn't too hard.
Setup django-reversion
First, install django-reversion
with the normal installation steps. Unfortunately, Wagtail won't tie into the same automatic magic that django-reversion
uses for the Django admin, but it has a stellar API you can call explicitly.
Register snippet models with django-reversion
For snippet models, you will need to add the @reversion.register()
decorator above the @register_snippet
in models.py
.
from django.db import models
import reversion
from wagtail.core.models import ClusterableModel, Orderable
from wagtail.snippets.models import register_snippet
@reversion.register()
@register_snippet
class Book(Orderable, ClusterableModel):
title = models.CharField(max_length=255)
Override the Wagtail views
Next, you will need to override the Wagtail views for the snippets you want to audit in wagtail_hooks.py
.
from reversion.revisions import add_to_revision, create_revision, set_comment, set_user
from wagtail.contrib.modeladmin.options import ModelAdmin
from wagtail.contrib.modeladmin.views import CreateView, EditView
from .models import Book
class RevisionEditView(EditView):
def form_valid(self, form, *args, **kwargs):
form_valid_return = super().form_valid(form, *args, **kwargs)
with create_revision():
set_comment(self.get_success_message(self.instance))
add_to_revision(self.instance)
set_user(self.request.user)
return form_valid_return
class RevisionCreateView(CreateView):
def form_valid(self, form, *args, **kwargs):
form_valid_return = super().form_valid(form, *args, **kwargs)
# Call form.save() explicitly to get access to the instance
instance = form.save()
with create_revision():
set_comment(self.get_success_message(instance))
add_to_revision(instance)
set_user(self.request.user)
return form_valid_return
class BookAdmin(ModelAdmin):
model = Book
edit_view_class = RevisionEditView
create_view_class = RevisionCreateView
Happy trails
Overriding the ModelAdmin classes by setting the edit_view_class
and create_view_class
settings and django-revision
is the real magic here. But, be sure to note the weirdness in the CreateView
to get an instance
that is usable. In testing it doesn't create multiple instances of the model, however, I would not be surprised if there are duplicate database calls because of the implementation.
Tested with the following package versions
- Python 3.7.4
- Django==2.2.4
- wagtail==2.6.1
- django-reversion==3.0.4
Originally published on adamghill.com.
Top comments (2)
Hi,
this post helped me a lot overriding the EditView for a ModelAdmin on wagtail.
My problem is I want to trigger actions the moment the user clicks 'edit' item from the IndexView, not at the time of form_validation, and I can't manage to find the method to override
Ok found I can override dispatch method for actions after Edit button is clicked from an item on the ModelAdmin IndexView.
Also can override form_valid method also inside the wagtail_hook class for my model, for actions with the changed fields on the EditView form, I needed to sync this info back to another corporate service API