将 link 可用的页面过滤到另一个页面

Filter the pages available to link to another page

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

这是我的代码:

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 %}

这对于 PageChooserPanel 来说并不容易,它会打开带有搜索界面的模式,但是如果您对字段只显示一个下降感到满意,那么您实现这个目标会容易得多下 'sibling' 页。

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

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

例子

  • 下面我们将创建一个自定义 FieldPanel 来修改其 on_form_bound 的行为,一旦表单可用,就会在为编辑器构建表单时调用它。
  • 从这里我们可以找到该页面列表字段的字段,并将其查询集修改为所需的结果。
  • page = self.page 将在您未使用 InlinePanel 时工作,因为实例将是当前编辑的页面。
  • 但是,对于 InlinePanel,我们需要考虑准备作为模板的 'initial' 表单的情况,以便您可以动态添加项目。
  • 为了处理 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