Ветвление рабочих процессов на основе значения указанного поля Page

У меня есть модель DailyReflectionPage с полем reflection_date, которое формирует основу для slug страницы, имеющего форму YYYY-MM-DD. Вот выдержка из моей модели Page:

class DailyReflectionPage(Page):
    """
    The Daily Reflection Model
    """
    ...
    ...

    reflection_date = models.DateField("Reflection Date", max_length=254)
    ...
    ...
    @cached_property
    def date(self):
        """
        Returns the Reflection's date as a string in %Y-%m-%d format
        """
        fmt = "%Y-%m-%d"
        date_as_string = (self.reflection_date).strftime(fmt)
        return date_as_string      
    ...
    ...
    def full_clean(self, *args, **kwargs):
        # first call the built-in cleanups (including default slug generation)
        super(DailyReflectionPage, self).full_clean(*args, **kwargs)

        # now make your additional modifications
        if self.slug is not self.date:
            self.slug = self.date
    ...
    ...

Эти ежедневные размышления пишутся разными авторами в рамках буклета, который публикуется в конце года и предназначен для использования в следующем году. Я бы хотел, чтобы рабочий процесс, например, ежедневные размышления с января по июнь рассматривались одной группой, а с июля по декабрь - другой, как показано на схеме ниже:

Branching Workflows based on date

Как этого можно достичь?

Этого можно достичь, создав ОДИН новый тип рабочего процесса Task, который имеет отношение к двум наборам пользователей Group (например, a/b или before/after, вероятно, лучше оставить это общим в определении модели).

Этот новый Task может быть создан как часть нового Workflow в админке Wagtail, и каждая из групп, связанных с группой модератора 1 / 2.

Методы Wagtail на Task позволяют возвращать варианты утверждения на основе модели Page для любого созданного рабочего процесса, отсюда вы можете искать метод, который был бы на классе, и назначать группы оттуда.

Преимущества более общего подхода в том, что вы можете использовать его для любого разделения заданий модераторов в рамках будущих задач Workflow.

Обзор внедрения

  • 1 - прочитайте Wagatail Docs о том, как добавить новый тип задачи и Task model reference, чтобы понять этот процесс.
  • 2 - Прочитайте полную реализацию в коде встроенного GroupApprovalTask.
  • 3 - В GroupApprovalTask вы можете увидеть, что методы с переопределениями все полагаются на проверку self.groups, но все они получают page, переданные в качестве аргумента этим методам.
  • 4 - Создайте новую модель Task, расширяющую класс Wagtail Task и на этой модели создайте две модели ManyToManyField, позволяющие связать два набора групп пользователей (примечание: не обязательно делать это в виде двух полей, можно поместить модель в середину, но приведенный ниже пример - это просто простейший способ добраться до гала).
  • 5 - На модели DailyReflectionPage создайте метод get_approval_group_key, который будет возвращать, возможно, простое булево или 'A' или 'B' на основе бизнес-требований, описанных вами выше (проверка даты модели и т.д.)
  • 6 - В вашем пользовательском Task создайте метод, который абстрагирует проверку Page для этого метода и возвращает группу пользователей Задачи. Возможно, вы захотите добавить некоторую обработку ошибок и значения по умолчанию. Например, get_approval_groups
  • .
  • 7 - Добавьте пользовательский метод для каждого из методов 'start', 'user_can_access_editor', page_locked_for_user, user_can_lock, user_can_unlock, get_task_states_user_can_moderate, который вызывает get_approval_group со страницей и возвращает значения (см. код GroupApprovalTask для того, что они должны делать.
  • ).

Примеры фрагментов кода

models.py


class DailyReflectionPage(Page):
    """
    The Daily Reflection Model
    """
    def get_approval_group_key(self):
        # custom logic here that checks all the date stuff
        if date_is_after_foo:
            return 'A'
        return 'B'    


class SplitGroupApprovalTask(Task):

    ## note: this is the simplest approach, two fields of linked groups, you could further refine this approach as needed.

    groups_a = models.ManyToManyField(
        Group,
        help_text="Pages at this step in a workflow will be moderated or approved by these groups of users",
        related_name="split_task_group_a",
    )
    groups_b = models.ManyToManyField(
        Group,
        help_text="Pages at this step in a workflow will be moderated or approved by these groups of users",
        related_name="split_task_group_b",
    )

    admin_form_fields = Task.admin_form_fields + ["groups_a", "groups_b"]
    admin_form_widgets = {
        "groups_a": forms.CheckboxSelectMultiple,
        "groups_b": forms.CheckboxSelectMultiple,
    }

    def get_approval_groups(self, page):
       """This method gets used by all checks when determining what group to allow/assign this Task to"""
        
        # recommend some checks here, what if `get_approval_group` is not on the Page?
        approval_group = page.specific.get_approval_group_key()

        if (approval_group == 'A'):
            return self.group_a

        return self.group_b

    # each of the following methods will need to be implemented, all checking for the correct groups for the Page when called
    # def start(self, ...etc)
    # def user_can_access_editor(self, ...etc)
    # def page_locked_for_user(self, ...etc)
    # def user_can_lock(self, ...etc)
    # def user_can_unlock(self, ...etc)
    # def get_task_states_user_can_moderate(self, ...etc)

    def get_actions(self, page, user):
        # essentially a copy of this method on `GroupApprovalTask` but with the ability to have a dynamic 'group' returned.
        approval_groups = self.get_approval_groups(page)

        if approval_groups.filter(id__in=user.groups.all()).exists() or user.is_superuser:
            return [
                ('reject', "Request changes", True),
                ('approve', "Approve", False),
                ('approve', "Approve with comment", True),
            ]

        return super().get_actions(page, user)


Вернуться на верх