【问题标题】:Filter the pages available to link to another page过滤可链接到另一个页面的页面
【发布时间】:2023-03-23 11:51:01
【问题描述】:

我对 Wagtail 真的很陌生。我一直在尝试找到一种方法来过滤选择器(PageChooserPanel)中的值。我正在建立一个故事网站,作者可以在其中创建非线性故事。我遵循一个博客模型来构建它并对其进行扩展。我已经到了作者可以通过可订购链接链接页面的地步。问题是其他故事的可订购显示页面。有没有办法过滤掉不相关的页面。我很感激这方面的任何帮助。提前谢谢!

这是我的代码:

class StoryPath(models.Model):
    route = models.ForeignKey('story.StoryPage', on_delete=models.SET_NULL, null=True, related_name='next_path', verbose_name='Story Path')
    
    panels = [
        PageChooserPanel('route', page_type='story.StoryPage'),
        FieldPanel('correct'),
    ]

    class Meta:
        abstract = True

class StoryPathOrderable(Orderable, StoryPath):
    page = ParentalKey('story.StoryPage', on_delete=models.CASCADE, related_name='story_paths')

class StoryPage(Page):
    template = 'story/story_page.html'
    body = RichTextField()
    
    content_panels = [
        FieldPanel('title', heading='Section Title', classname='collapsible'),
        FieldPanel('body', classname='collapsible'),
        MultiFieldPanel(
            [
                InlinePanel('story_paths'),
            ],
            heading = 'Story Paths',
            classname = 'collapsible'
        )
    ]
    parent_page_type =['story.Story']
    subpage_types = []

    def __str__(self):
        return '%s' % (self.title)

class Story(Page):
    subtitle = models.CharField(max_length=250, null=True, blank=True)
    
    content_panels = Page.content_panels + [
        FieldPanel('subtitle'),
    ]

    subpage_types = ['story.StoryPage']

    def __str__(self):
        return '%s' % (self.title)

编辑:这是我正在使用的模板:

{% extends "base.html" %}
{% load static wagtailcore_tags %}

{% block body_class %}{{self.title}}{% endblock %}

{% block extra_css %}{% endblock extra_css %}

{% block content %}
    <div class="d-flex justify-content-center flex-column">
        <div class="fs-3">{{page.title}}</div>
        <div>{{page.body|richtext}}</div>
    </div>
{% endblock content %}

【问题讨论】:

  • 您能否更详细地更新问题,最好是您正在使用的模板代码,或者如果您还没有得到您希望输出数据看起来如何的文本表示。看起来 StoryPage 应该有一个 story_paths 的值,并且其中的每个值都应该有 next_page ,它本身就是另一个 StoryPage。
  • 我已经发布了模板代码。不过,我目前正在管理员上进行测试创建/编辑。现在,管理站点就足够了。
  • 哦,好吧 - 目标是确保 StoryPage 的页面选择器仅显示其他“兄弟”StoryPages 作为选项吗?例如,如果您有 Horror Story 和 Ghost Story,每个 StoryPage 下都有多个 StoryPage,那么作为 Horror Story 子项的 StoryPage 应该只允许您选择 Horror Story 下的其他 StoryPages 而不是 Gost 故事?
  • 是的,这样作者就不会混淆其他故事的页面。因此,即使作者有两个包含多个 StoryPage 的故事,如果他正在编辑 Story1 的 StoryPage,他也只能在 Orderable 中看到其兄弟页面。同样,如果他正在处理 Story2 的 StoryPage,他只能看到 Story2 的 StoryPage 的兄弟页面。

标签: wagtail wagtail-admin


【解决方案1】:

这对于PageChooserPanel 来说并不容易,它会打开一个带有搜索界面的模式,但是如果您愿意该字段仅显示“兄弟”的下拉列表,那么您可以更轻松地实现此目标' 页。

对这一切如何运作的一些概述;

  • 当您使用 InlinePanel('story_paths'), 时,它会利用 Wagtail、Django 和 Django Modelcluster 的一些部分来设置动态表单集。
  • InlinePanel 允许您为类似对象的多个添加创建子“表单”,在这种情况下,内联创建的对象是 StoryPathOrderable 实例。
  • 当您使用 InlinePanel 时,它会在正在内联创建/管理的内部模型上查找 panels 属性。
  • 在您的内部模型上,您已设置PageChooserPanel('route', page_type='story.StoryPage'),
  • PageChooserPanel 对于很多用例来说确实很棒,但对于边缘情况有点难以自定义(因为它创建了一个字段来触发具有自己的搜索/列表界面的模式)。可以使用 Wagtail Hooks 修改此模式的结果 - 请参阅 `construct_page_chooser_queryset',但这是全局性的,无法知道“哪个”页面或字段正在请求链接页面。
  • 但是,我们不必使用PageChooserPanel,您可以使用基本的FieldPanel,它提供了整个应用程序可用页面的下拉列表,我们可以更轻松地自定义此字段的查询。
  • 如果您想对此进行更多控制并希望保留模态界面,您可以使用Wagtail Generic Chooser 查看。

示例

  • 下面我们将创建一个自定义FieldPanel 来修改其on_form_bound 的行为,一旦表单可用,就会在为编辑器构建表单时调用它。
  • 从这里我们可以找到该页面列表字段的字段并将其查询集修改为所需的结果。
  • page = self.page 将在您不使用 InlinePanel 时起作用,因为实例将是当前编辑的页面。
  • 但是,对于 InlinePanel,我们需要考虑将“初始”表单准备为模板的情况,以便您可以动态添加项目。
  • 为了处理InlinePanel 和基本字段的使用,我们可以获取绑定到自定义面板实例的当前请求并从中推断页面。
  • 一旦我们可以访问正确的Page 和字段,我们就可以修改查询集以满足我们的需要,Wagtail 扩展查询集的能力以添加child_of and sibling_of

some-app/models.py

from wagtail.admin.edit_handlers import FieldPanel



class SiblingOnlyPageFieldPanel(FieldPanel):
    def on_form_bound(self):
        super().on_form_bound()

        field = self.form[self.field_name].field

        # when creating a new page, check for the parent page ID & refine field queryset
        parent_page_id = self.request.resolver_match.kwargs.get("parent_page_id", None)
        if parent_page_id:
            parent_page = Page.objects.filter(pk=parent_page_id).first()
            field.queryset = field.queryset.child_of(parent_page)
            return

        # when editing a page, get the ID of the page currently being edited
        page_id = self.request.resolver_match.kwargs.get("page_id", None)
        if not page_id:
            return

        page = Page.objects.filter(pk=page_id).first()
        if not page:
            return

        field = self.form[self.field_name].field
        field.queryset = field.queryset.sibling_of(page)



class StoryPath(models.Model):
    route = models.ForeignKey('story.StoryPage', on_delete=models.SET_NULL, null=True, related_name='next_path', verbose_name='Story Path')
    
    panels = [
        SibingOnlyPageFieldPanel('route'), # replaced PageChooserPanel & removed `page_type` as that will no longer work for a normal FieldPanel
        FieldPanel('correct'),
    ]

    class Meta:
        abstract = True

【讨论】:

  • 非常感谢您的解释!它阐明了 Wagtail Orderables 的工作原理。我还深入研究了 Wagtail 代码,看看它是如何工作的。
  • 只是一个更新:我尝试了这段代码,但就像你说的,我需要获取StoryPathOrderable 的页面字段。我在想我可以通过获取可订购的实例然后从那里获取页面来做到这一点。但是,由于尚未生成可订购对象,因此它返回 None。我现在正试图找出一种方法来获取StoryPage 的实例以获取页面,然后它是兄弟姐妹,但可订购对象似乎不允许我访问该实例。
  • 我尝试查找 self.instance.page 并收到错误 story.models.StoryPathOrderable.page.RelatedObjectDoesNotExist: StoryPathOrderable has no page. 是否有办法将 StoryPage 数据发送到可订购对象或从可订购对象中获取 StoryPage 实例?
  • @ron_e_e - 我已经更新了适用于 InlinePanel 使用的答案,并检查了一些边缘情况,例如创建新页面。希望这会有所帮助。如果您遇到困难,只需在 on_form_bound 中打印出 vars(instance) 以查看您可以访问的内容,此外您还可以在此处查看 InlinePanel 和 FieldPanel 的完整代码 - github.com/wagtail/wagtail/blob/main/wagtail/admin/…
猜你喜欢
  • 2013-12-13
  • 2010-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
相关资源
最近更新 更多