首页 > 解决方案 > 如何将页面隐私复制到 Wagtail 中的 Block?

问题描述

页面具有此隐私设置,您可以在其中设置允许查看特定页面的人员。

我的文档需要更细粒度的控制,对于特定的块,也可以设置限制其可见性。

我会很满足于组选择器,但显然我不能在 Page 模型中放置 ManyToManyRelation。相反,我会在需要特别处理的块中进行。

所以我尝试像这样定义块:

class MyBlock(StructBlock):
    visible_groups = ListBlock(ChooserBlock(
        label=_('Limit view to groups'), target_model='django.contrib.auth.groups', 
        required=False, blank=True
    ))

但是 Wagtail 抱怨说选择器块被声明时没有 target_model 属性。我猜缺少构造函数这样做足以表明 ChooserBlock 本身不适用于这样的操作。

如何正确声明我的 Block 以便我可以选择组,然后在渲染时正确识别它们并将它们与用户组匹配?

标签: wagtailwagtail-streamfield

解决方案


ChooserBlock并不是要单独使用,而是要用于构建其他类型的选择器的基类。

选项 0 - 使用相关模型

  • 虽然我理解您不想使用模型关系的愿望,但您最终可能会通过尝试在此处使用 StreamFields 来创建很多复杂性。
  • 在基本关系方面重新考虑问题可能是值得的,您的页面作为内容项列表,每个内容项与身份验证组具有一对多的关系并且具有一些内容(富文本等)。
  • 您可以使用与您的页面相关的普通模型来执行此操作,并使用 Wagtail 的InlinePanel允许用户创建许多这些内容项,每个内容项都有自己的身份验证组集和内容。请记住,其他模型上的内部内容仍然可以基于 StreamField,但此方法中的主要连接将是正常的 Django 关系。
  • 请记住,StreamField 链接不是那么健壮(例如,如果 auth 组被删除,链接可能会在您的 StreamField 中变得陈旧,并且没有简单的方法来阻止它),如果您想在未来。

选项 1 - 使用SnippetChooser

最快的方法是使用SnippetChooserBlockDjango auth 组并将其注册为一个片段。这种方法的一个警告是,该模型将在管理 UI 的片段菜单(如果启用)中可见。

首先,将模型注册为中央某处的片段(例如wagtail_hooks.py

from django.contrib.auth.models import Group
from wagtail.snippets.models import register_snippet

# ... other hooks things
register_snippet(Group)

StructBlock然后使用SnippetChooser您保存 Blocks 的任何位置构建您的自定义。

from django.contrib.auth.models import Group
from wagtail.snippets.blocks import SnippetChooserBlock
from wagtail.core.blocks import ListBlock

class PrivacyBlock(StructBlock):
    visible_groups = ListBlock(
        SnippetChooserBlock(
            Group,
            label='Limit view to groups',
            required=False,
            blank=True
        )
    )

    class Meta:
        icon = "user"

最后,您可以像任何其他 StructBlock 一样在 PageModel 中使用它。

from wagtail.admin.edit_handlers import FieldPanel, InlinePanel, StreamFieldPanel
from wagtail.core.fields import StreamField

from myapp.blocks import PrivacyBlock


class BlogPage(Page):
    # ... other fields

    privacy_content = StreamField([
        ('privacy', PrivacyBlock(blank=True))
    ])

    content_panels = Page.content_panels + [
        # ... other panels
        StreamFieldPanel('privacy_content'),
    ]

选项 2 - 构建或使用通用模型选择

您可以扩展 ChooserBlock 以构建您自己的选择器,但这会很快变得复杂,因为您还需要构建自定义模型选择器。下面的代码功能不完整,但可以让您了解可能需要什么。

如果您可以使用其他库,则可能值得考虑添加 Wagtail 通用选择器,这是来自 Google 的一些快速结果(我没有使用这些)。

请记住,选择器块特定于 StreamField 实现,并且选择器类似于特定类型的 Django 小部件。如果您想为此实现使用 StreamFields,您将需要构建或获取两者。

如果您需要构建自己的,我建议您深入了解 Wagtail 的内部结构,以了解其他选择器是如何构建的。

POC(非功能性)代码示例


from wagtail.admin.widgets import AdminChooser
from django.utils.functional import cached_property

class ModelChooser(AdminChooser):
    # ... implement __init__ etc


class AuthGroupBlock(ChooserBlock):

    @cached_property
    def target_model(self):
        from django.contrib.auth.models import Group
        return Group

    @cached_property
    def widget(self):
        return ModelChooser(self.target_model)


class PrivacyBlock(StructBlock):
    visible_groups = ListBlock(
        AuthGroupBlock(
            label='Limit view to groups',
            required=False,
            blank=True
        )
    )

    class Meta:
        icon = "user"


class PrivacyBlock(StructBlock):
    visible_groups = ListBlock(
        SnippetChooserBlock(
            Group,
            label='Limit view to groups',
            required=False,
            blank=True
        )
    )

    class Meta:
        icon = "user"


推荐阅读