From 0c168e302a0b0fec0e9a0dbc2a5867c5bdd9797d Mon Sep 17 00:00:00 2001 From: Matt Westcott <matt@west.co.tt> Date: Tue, 18 Feb 2025 00:01:32 +0000 Subject: [PATCH] Move LockableMixin to locking submodule --- wagtail/models/__init__.py | 77 +---------------------------------- wagtail/models/locking.py | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 75 deletions(-) create mode 100644 wagtail/models/locking.py diff --git a/wagtail/models/__init__.py b/wagtail/models/__init__.py index 163c0edb0a..7b25578484 100644 --- a/wagtail/models/__init__.py +++ b/wagtail/models/__init__.py @@ -67,7 +67,7 @@ from wagtail.coreutils import ( ) from wagtail.fields import StreamField from wagtail.forms import TaskStateCommentForm -from wagtail.locks import BasicLock, WorkflowLock +from wagtail.locks import WorkflowLock from wagtail.log_actions import log from wagtail.query import PageQuerySet, SpecificQuerySetMixin from wagtail.search import index @@ -106,6 +106,7 @@ from .i18n import ( # noqa: F401 bootstrap_translatable_model, get_translatable_models, ) +from .locking import LockableMixin from .media import ( # noqa: F401 BaseCollectionManager, Collection, @@ -291,80 +292,6 @@ class PageBase(models.base.ModelBase): PAGE_MODEL_CLASSES.append(cls) -class LockableMixin(models.Model): - locked = models.BooleanField( - verbose_name=_("locked"), default=False, editable=False - ) - locked_at = models.DateTimeField( - verbose_name=_("locked at"), null=True, editable=False - ) - locked_by = models.ForeignKey( - settings.AUTH_USER_MODEL, - verbose_name=_("locked by"), - null=True, - blank=True, - editable=False, - on_delete=models.SET_NULL, - related_name="locked_%(class)ss", - ) - locked_by.wagtail_reference_index_ignore = True - - class Meta: - abstract = True - - @classmethod - def check(cls, **kwargs): - return [ - *super().check(**kwargs), - *cls._check_revision_mixin(), - ] - - @classmethod - def _check_revision_mixin(cls): - mro = cls.mro() - error = checks.Error( - "LockableMixin must be applied before RevisionMixin.", - hint="Move LockableMixin in the model's base classes before RevisionMixin.", - obj=cls, - id="wagtailcore.E005", - ) - - try: - if mro.index(RevisionMixin) < mro.index(LockableMixin): - return [error] - except ValueError: - # LockableMixin can be used without RevisionMixin. - return [] - - return [] - - def with_content_json(self, content): - """ - Similar to :meth:`RevisionMixin.with_content_json`, - but with the following fields also preserved: - - * ``locked`` - * ``locked_at`` - * ``locked_by`` - """ - obj = super().with_content_json(content) - - # Ensure other values that are meaningful for the object as a whole (rather than - # to a specific revision) are preserved - obj.locked = self.locked - obj.locked_at = self.locked_at - obj.locked_by = self.locked_by - - return obj - - def get_lock(self): - """ - Returns a sub-class of ``BaseLock`` if the instance is locked, otherwise ``None``. - """ - if self.locked: - return BasicLock(self) - - class WorkflowMixin: """A mixin that allows a model to have workflows.""" diff --git a/wagtail/models/locking.py b/wagtail/models/locking.py new file mode 100644 index 0000000000..351f999b16 --- /dev/null +++ b/wagtail/models/locking.py @@ -0,0 +1,82 @@ +from django.conf import settings +from django.core import checks +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from wagtail.locks import BasicLock + +from .revisions import RevisionMixin + + +class LockableMixin(models.Model): + locked = models.BooleanField( + verbose_name=_("locked"), default=False, editable=False + ) + locked_at = models.DateTimeField( + verbose_name=_("locked at"), null=True, editable=False + ) + locked_by = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_("locked by"), + null=True, + blank=True, + editable=False, + on_delete=models.SET_NULL, + related_name="locked_%(class)ss", + ) + locked_by.wagtail_reference_index_ignore = True + + class Meta: + abstract = True + + @classmethod + def check(cls, **kwargs): + return [ + *super().check(**kwargs), + *cls._check_revision_mixin(), + ] + + @classmethod + def _check_revision_mixin(cls): + mro = cls.mro() + error = checks.Error( + "LockableMixin must be applied before RevisionMixin.", + hint="Move LockableMixin in the model's base classes before RevisionMixin.", + obj=cls, + id="wagtailcore.E005", + ) + + try: + if mro.index(RevisionMixin) < mro.index(LockableMixin): + return [error] + except ValueError: + # LockableMixin can be used without RevisionMixin. + return [] + + return [] + + def with_content_json(self, content): + """ + Similar to :meth:`RevisionMixin.with_content_json`, + but with the following fields also preserved: + + * ``locked`` + * ``locked_at`` + * ``locked_by`` + """ + obj = super().with_content_json(content) + + # Ensure other values that are meaningful for the object as a whole (rather than + # to a specific revision) are preserved + obj.locked = self.locked + obj.locked_at = self.locked_at + obj.locked_by = self.locked_by + + return obj + + def get_lock(self): + """ + Returns a sub-class of ``BaseLock`` if the instance is locked, otherwise ``None``. + """ + if self.locked: + return BasicLock(self)