Sage Abdullah
JSONFieldSage Abdullah
JSONFieldAn open source content management system (CMS) built on Django.
The Page model acts as both "model" and "view". The tree structure of the model is reflected in your website’s URL structure.
Wagtail gives you the power to:
python -m pip install wagtailpython -m pip install wagtail
INSTALLED_APPSMIDDLEWARESurls.pyWagtail primarily works with its Page model. For other models, you can register them as "snippets".
Add a @register_snippet decorator to your model and specify the editable fields using panels.
from wagtail.admin.panels import FieldPanel from wagtail.snippets.models import register_snippet @register_snippet class Person(models.Model): name = models.CharField(max_length=255) description = models.TextField() ... # Optional, but allows you to fine-tune the editable fields and their order panels = [ FieldPanel("name"), FieldPanel("description"), ... ]from wagtail.admin.panels import FieldPanel from wagtail.snippets.models import register_snippet @register_snippet class Person(models.Model): name = models.CharField(max_length=255) description = models.TextField() ... # Optional, but allows you to fine-tune the editable fields and their order panels = [ FieldPanel("name"), FieldPanel("description"), ... ]
The preview feature comes from the PreviewableMixin class.
To use it, add it as a superclass to your model. Then, override the get_preview_template and get_preview_context methods.
from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): ... def get_preview_template(self, request, preview_mode): return f"products/detail.html" def get_preview_context(self, request, preview_mode): return {"product": self}from wagtail.models import PreviewableMixin @register_snippet class Product(PreviewableMixin, models.Model): ... def get_preview_template(self, request, preview_mode): return f"products/detail.html" def get_preview_context(self, request, preview_mode): return {"product": self}
You can define different preview modes using the preview_modes attribute.
... class Product(PreviewableMixin, models.Model): ... preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}... class Product(PreviewableMixin, models.Model): ... preview_modes = [ ("index", "Index"), ("detail", "Detail"), ] def get_preview_template(self, request, preview_mode): return f"products/{preview_mode}.html" def get_preview_context(self, request, preview_mode): if preview_mode == "index": return {"products": [self]*20} return {"product": self}
You can have revisions for your model by extending the RevisionMixin class.
from wagtail.models import RevisionMixin ... class Product(RevisionMixin, PreviewableMixin, models.Model): ...from wagtail.models import RevisionMixin ... class Product(RevisionMixin, PreviewableMixin, models.Model): ...
Then, create and and run the migrations.
python manage.py makemigrations python manage.py migratepython manage.py makemigrations python manage.py migrate
This allows you to compare the changes between revisions and revert to a previous revision.
Wagtail allows you to save unpublished changes ("drafts") of your models. This functionality is provided by the DraftStateMixin.
from wagtail.models import DraftStateMixin ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ...from wagtail.models import DraftStateMixin ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ...
After creating and running the migrations, you can make use of the live field of the model.
Product.objects.filter(live=True)Product.objects.filter(live=True)
Unpublished changes are saved as Revisions and will not be reflected to your model’s instance until you publish them.
To enable the scheduled publishing feature, add the PublishingPanel to your model’s panels.
from wagtail.admin.panels import PublishingPanel ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ... panels = [ ... PublishingPanel(), ]from wagtail.admin.panels import PublishingPanel ... class Product(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model): ... panels = [ ... PublishingPanel(), ]
You can prevent multiple users from editing the same model instance at the same time by using the LockableMixin class.
from wagtail.models import LockableMixin ... class Product( DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...from wagtail.models import LockableMixin ... class Product( DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
After making and running the migrations, Wagtail will give you the option to lock the model instance.
Wagtail allows you to define workflows for your models, which can be used to moderate content changes before they go live. This functionality is provided by the WorkflowMixin class.
from wagtail.models import WorkflowMixin ... class Product( WorkflowMixin, DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...from wagtail.models import WorkflowMixin ... class Product( WorkflowMixin, DraftStateMixin, LockableMixin, RevisionMixin, PreviewableMixin, models.Model, ): ...
By default, Wagtail gives you the "Moderators approval" workflow, which requires a moderator to approve the changes before they go live.
You can create custom workflows with custom tasks to suit your specific needs.
Wagtail allows further customisations to the views for your models by using a custom SnippetViewSet subclass and registering that instead. It is similar to Django’s ModelAdmin class.
# wagtail_hooks.py from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet from products.models import Product class ProductViewSet(SnippetViewSet): model = Product search_fields = ["name"] list_display = ["name", "final_price", "stock"] list_export = ["name", "price", "discount", "final_price", "stock"] list_filter = {"discount": ["gte"], "stock": ["lte"]} register_snippet(ProductViewSet)# wagtail_hooks.py from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet from products.models import Product class ProductViewSet(SnippetViewSet): model = Product search_fields = ["name"] list_display = ["name", "final_price", "stock"] list_export = ["name", "price", "discount", "final_price", "stock"] list_filter = {"discount": ["gte"], "stock": ["lte"]} register_snippet(ProductViewSet)
Learn more at wagtail.org
Slides available at slides.laymonage.com/modern-wagtail
Code example available at github.com/laymonage/modern-wagtail
Reach out to [email protected]
Thanks to Storm and Thibaud for helping with ideas for this talk!