Snippet `ManyToManyField` fields not handled properly by `RevisionMixin` and `DraftStateMixin`
See original GitHub issueIssue Summary
Changes to a snippet’s ManyToManyField
selections are ignored by RevisionMixin
. If DraftStateMixin
is also used, those changes are lost when saving.
Steps to Reproduce
With the following models, do the following in the Wagtail admin:
- Create some
Category
objects - Create a
TestSnippet
object - Edit the created
TestSnippet
, and change itsCategory
selections - Open the snippet again, and verify the changes have been saved
- View the revision history and compare the edited version to the original one
- Expected output: category changes. Actual output: “There are no differences between these two versions”.
Now, also add in DraftStateMixin
, migrate, and then:
- Edit the snippet’s categories
- Press Publish or Save draft
- Open the snippet again
- Expected outcome: the changes were saved. Actual outcome: the changes have disappeared.
@register_snippet
class Category(models.Model):
name = models.CharField(max_length=255)
panels = [FieldPanel('name')]
@register_snippet
class TestSnippet(RevisionMixin, models.Model):
title = models.CharField(max_length=255)
categories = models.ManyToManyField('app.Category', blank=True)
_revisions = GenericRelation('wagtailcore.Revision', related_query_name='test_snippet')
panels = [
FieldPanel('title'),
FieldPanel('categories', widget=forms.CheckboxSelectMultiple),
]
@property
def revisions(self):
return self._revisions
Technical details
Django 4.1.3 Python 3.9.9 wagtail 4.1.1.final.1
Issue Analytics
- State:
- Created 10 months ago
- Comments:5 (2 by maintainers)
Top Results From Across the Web
Mika Raunio mikaraunio - GitHub
Snippet ManyToManyField fields not handled properly by RevisionMixin and DraftStateMixin. Issue Summary Changes to a snippet's ManyToManyField selections ...
Read more >Snippets — Wagtail Documentation 4.2a0 documentation
Snippets do not use multiple tabs of fields, nor do they provide the “save as draft” or ... template tags like pageurl need...
Read more >django-reversion revert ManyToMany fields outside admin
If I understand it correctly, I think you should get the revision for the version; the version contains the data of the object,...
Read more >Release 4.1.1 Torchbox - Wagtail Documentation
When blank=True, it means that this field is not required and can be empty. ... from wagtail.models import DraftStateMixin, RevisionMixin.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Thank you @mikaraunio! Pretty sure that
ParentalKey
andParentalManyToManyField
only changes the application-level representation of the fields, (i.e. the database representation is the same as that ofForeignKey
andManyToManyField
), so there should be no data loss (as you confirmed).For slightly more technical details, the
RevisionMixin
relies on theserializable_data()
andfrom_serializable_data()
methods to serialize/deserialize the fields (including relations) of the model instance. By default, it tries to call those methods from the superclass, and fall back toget_serializable_data_for_fields()
andmodel_from_serializable_data()
functions from django-modelcluster if none of the superclasses have the method:https://github.com/wagtail/wagtail/blob/119f288a3cf91775687aafffe19ac4c92bb45e0a/wagtail/models/__init__.py#L278-L291
In django-modelcluster’s
ClusterableModel
, the methods are defined to handle the relations, and this is “where the magic happens” to make relations stored in the revisions correctly:https://github.com/wagtail/django-modelcluster/blob/8666f16eaf23ca98afc160b0a4729864411c0563/modelcluster/models.py#L209-L273
So, yes, I believe this is the officially supported way. PR for documentation up at #9691.
Confirm that switching from
Model
toClusterableModel
seems to make everything work here.Also, have now switched over all my existing snippets to
ParentalManyToManyField
and migrated, and not seeing any data loss or oddities.Would be good to have official confirmation that this is the way to go, and agree the docs should then be updated.
For others who might be facing this, here’s the “works for me” snippet: