Django Wagtail динамическое меню на основе пользователя и роли пользователя

Использование Django-Wagtail и Wagtail Menu's. Я хочу построить систему со следующими типами характеристик.

  1. У меня есть класс пользователей (Vendor, Supplier)
  2. .
  3. Каждый созданный пользователь должен быть одним из этих двух классов.
  4. Каждый класс пользователей имеет различные роли, и эти роли отличаются от класса к классу. Например. Поставщик: финансы, склад, администратор Поставщик: финансы, склад, администратор, водитель.

Я создал класс UserType и оба класса "Vendor" и "Supplier" наследуются от этого класса. Затем я добавил поле в класс User, которое выбирает тип пользователя.

class UserType(model.Model):
    common fields.......

class Vendor(UserType):
    child_vendor_only_fields.....

class Supplier(UserType):
    child_supplier_only_fields.....

class User(AbstractUser):
    userVar = models.ForeignKey(UserType, on_delete=models.SET_NULL, null=True, blank=True)

Я также использовал админку wagtail для создания пользовательских ролей для каждого из этих классов, как описано выше.

Очевидно, что то, что "Поставщики" могут создавать, читать, обновлять и удалять, отличается от того, что может делать "Поставщик". И еще в рамках этого то, что финансовая роль может делать в "Поставщике", отличается от того, что может делать "Склад".

Как мне создать динамическое меню, которое отображает различные меню для этих перестановок?

Мои первоначальные мысли были навеяны этим. Простое добавление поля выбора

USER_TYPE_CHOICES = (
('Vendor', 'Vendor'),
('Supplier', 'Suplier'))

class GenericPage(MenuPage):
    """
    This model will gain the fields, methods and `setting_panels` attribute
    from `MenuPage`, but `settings_panels` is being overridden to include
    other fields in the `Settings` tab.
    """
    availableTo = CharField(choices=USER_TYPE_CHOICES,)

    # 'menupage_panel' is a collapsible `MultiFieldPanel` with the important
    # fields already grouped together, making it easy to include in custom
    # panel definitions, like so:
    settings_panels = [
        FieldPanel('custom_settings_field_one'),
        menupage_panel
    ]

У меня два вопроса:

  1. Правильно ли я выбрал путь создания пользовательских меню для типов пользователей? Если да, то как мне потом сделать то же самое для ролей под типами пользователей?

  2. В плане CRUD я должен просто реализовать проверку каждый раз, когда пользователь делает грубое действие, может ли он зафиксировать это действие прагматически?

  3. Что касается CRUD, я не сторонник того, чтобы давать кому-то возможность сделать что-то, а потом сообщать, что эта возможность запрещена. Они могут потратить полчаса на создание/редактирование страницы, а потом узнать, что потратили это время впустую. Лучше сказать им об этом при загрузке страницы.

    Для типов пользователей я бы просто создал их как роли, в которые вы добавляете пользователей. Тогда у вас будет простой тест для user.groups, чтобы увидеть, какую роль они имеют.

    Вы можете легко разработать пользовательскую панель с ограничениями для каждого из типов панелей, которые вам нужно ограничить, проще, если все они FieldPanels. В классе BoundPanel у вас есть доступ к объекту запроса, поэтому вы можете извлечь объект пользователя из него во время выполнения и решить, что делать дальше.

    У Panel.BoundPanel есть метод is_shown(), поэтому вы можете создать пользовательскую панель, наследующую FieldPanel, переопределить этот метод, чтобы проверить, есть ли в запросе пользователя параметр авторизованного списка и установить true/false соответственно.

    # restricted_field_panel.py
    from wagtail.admin.panels import FieldPanel
    
    class RestrictedFieldPanel(FieldPanel):
        def __init__(self, field_name, authorised_groups, **kwargs):
            self.field_name = field_name
            self.authorised_groups = authorised_groups if isinstance(authorised_groups, list) else [authorised_groups]
            super().__init__(self.field_name, **kwargs)
    
        def clone_kwargs(self):
            kwargs = super().clone_kwargs()
            kwargs.update(
                authorised_groups=self.authorised_groups
            )
            return kwargs
    
        class BoundPanel(FieldPanel.BoundPanel):
            def is_shown(self):
                show_field = super().is_shown()
                is_authorised = self.request.user.groups.get_queryset().filter(name__in=self.panel.authorised_groups).exists()
                return (show_field and is_authorised)
    

    В модели страницы:

    content_panels = Page.content_panels + [
        RestrictedFieldPanel('some_vendor_field', 'Vendors'),
        RestrictedFieldPanel('some_supplier_field', 'Suppliers'),
        ....
    ]
    

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

    Другие типы панелей нужно наследовать и тестировать везде, где выполняется рендеринг.

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