1 changed files with 123 additions and 0 deletions
@ -0,0 +1,123 @@ |
|||||||
|
from django.conf import settings |
||||||
|
from django.db import models |
||||||
|
from django.utils import timezone |
||||||
|
from django.utils.translation import gettext_lazy as _ |
||||||
|
|
||||||
|
# |
||||||
|
# Workflow Helpers |
||||||
|
|
||||||
|
""" |
||||||
|
States: |
||||||
|
- inactive (either active=False or publication date not reached) |
||||||
|
- public (active or archived) |
||||||
|
- active (public, not archived) |
||||||
|
- archived (public, not active) |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
class WorkflowQuerySet(models.QuerySet): |
||||||
|
@classmethod |
||||||
|
def exclude_inactive_filter(cls): |
||||||
|
"""Usage: qs.exclude(**self.exclude_inactive_filter())""" |
||||||
|
return { |
||||||
|
"is_published": True, |
||||||
|
"publication_datetime__lte": timezone.now().date(), |
||||||
|
} |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def active_filter(cls): |
||||||
|
return { |
||||||
|
"is_published": True, |
||||||
|
"publication_datetime__lte": timezone.now().date(), |
||||||
|
"archiving_datetime__gte": timezone.now().date(), |
||||||
|
} |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def archived_filter(cls): |
||||||
|
return { |
||||||
|
"is_published": True, |
||||||
|
"publication_datetime__lte": timezone.now().date(), |
||||||
|
"archiving_datetime__lte": timezone.now().date(), |
||||||
|
} |
||||||
|
|
||||||
|
def public(self): |
||||||
|
# Active or archived |
||||||
|
return self.exclude(**self.exclude_inactive_filter()) |
||||||
|
|
||||||
|
def active(self): |
||||||
|
return self.filter(**self.active_filter()) |
||||||
|
|
||||||
|
def archived(self): |
||||||
|
return self.filter(**self.archived_filter()) |
||||||
|
|
||||||
|
|
||||||
|
class ManyToManyWorkflowQuerySet(WorkflowQuerySet): |
||||||
|
def _build_related_filters(self, filter_template): |
||||||
|
""" |
||||||
|
Transforms all filter rules to match any related models which |
||||||
|
itself is workflow managed. |
||||||
|
""" |
||||||
|
filter = {} |
||||||
|
for field in self.model._meta.get_fields(): |
||||||
|
if field.many_to_one and isinstance(field.related_model._meta.default_manager, WorkflowManager): |
||||||
|
field.name |
||||||
|
for path, value in filter_template.items(): |
||||||
|
filter[f'{field.name}__{path}'] = value |
||||||
|
return filter |
||||||
|
|
||||||
|
def public(self): |
||||||
|
# Active or archived |
||||||
|
return self.exclude(**self._build_related_filters(self.exclude_inactive_filter())) |
||||||
|
|
||||||
|
def active(self): |
||||||
|
return self.filter(**self._build_related_filters(self.active_filter())) |
||||||
|
|
||||||
|
def archived(self): |
||||||
|
return self.filter(**self._build_related_filters(self.archived_filter())) |
||||||
|
|
||||||
|
|
||||||
|
class WorkflowManager(models.Manager.from_queryset(WorkflowQuerySet)): |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class ManyToManyWorkflowManager(models.Manager.from_queryset(ManyToManyWorkflowQuerySet)): |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class WorkflowMixin(models.Model): |
||||||
|
creation_datetime = models.DateTimeField(auto_now_add=True) |
||||||
|
modification_datetime = models.DateTimeField(auto_now=True) |
||||||
|
creation_user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, |
||||||
|
editable=False, |
||||||
|
on_delete=models.SET_NULL, related_name='+') |
||||||
|
modification_user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, |
||||||
|
editable=False, |
||||||
|
on_delete=models.SET_NULL, related_name='+') |
||||||
|
|
||||||
|
is_published = models.BooleanField(_("Publicly visible"), default=False) |
||||||
|
publication_datetime = models.DateTimeField(_("Publication Date"), default=timezone.now) |
||||||
|
archiving_datetime = models.DateTimeField(_("Archiving Date"), default=timezone.datetime.max) |
||||||
|
|
||||||
|
objects = WorkflowManager() |
||||||
|
|
||||||
|
class Meta: |
||||||
|
abstract = True |
||||||
|
ordering = ['-publication_datetime'] # Most recent first |
||||||
|
|
||||||
|
@property |
||||||
|
def worflow_status(self): |
||||||
|
now = timezone.now() |
||||||
|
if not self.is_active or self.publication_datetime < now: |
||||||
|
return 'inactive' |
||||||
|
elif self.publication_datetime >= now and self.archiving_datetime < now: |
||||||
|
return 'active' |
||||||
|
else: |
||||||
|
return 'archived' |
||||||
|
|
||||||
|
|
||||||
|
def display_is_active(obj): |
||||||
|
return obj.is_publised and obj.publication_datetime <= timezone.now() |
||||||
|
|
||||||
|
|
||||||
|
display_is_active.boolean = True |
||||||
|
display_is_active.short_description = _("Aktiv") |
Loading…
Reference in new issue