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