为了账号安全,请及时绑定邮箱和手机立即绑定

在 Wagtail 中实现文章和页面模型之间的一对多

在 Wagtail 中实现文章和页面模型之间的一对多

jeck猫 2022-06-14 09:51:16
我正在尝试使用文章到页面结构设置 Wagtail 网站,但我正在努力。例如,一篇评论文章可能有一个介绍页面、一个基准页面和一个结论页面。我想弄清楚如何在 wagtail 中允许这种关系并拥有它,以便编辑可以将多个页面添加到同一页面上的同一篇文章中。我可以想象页面界面看起来有点像你在页面上的内容、推广和设置,但具有添加、重命名和重新排序页面的能力。我尝试在链接到文章的页面模型上使用外键,但我无法以我想要的方式在管理员中显示它。这是我想要使用的模型布局的 django 版本。您有一篇由一页或多页组成的父文章。这些页面应该是可编辑的、可排序的,并且可以在管理员的一个面板中使用流域创建:Class Article(models.Model)    STATE_DRAFT = 0    STATE_REVIEW= 1    STATE_PUBLICATION = 2    STATE_HIDDEN = 3    STATE = (        (STATE_DRAFT, 'draft'),        (STATE_REVIEW, 'pending review'),        (STATE_PUBLICATION, 'ready for publication'),        (STATE_HIDDEN, 'hide and ignore'),    )    title = models.CharField(_('title'), max_length=256)    slug = models.SlugField(        _('slug'), unique=True, blank=True, default='', max_length=256    )    description = models.TextField(        _('description'), max_length=256, blank=True, default=''    )    author = models.ForeignKey(        User, on_delete=models.CASCADE, related_name='article'    )    publication = models.DateTimeField(        null=True, blank=True, default=None, db_index=True, help_text='''            What date and time should the article get published        '''    )    state = models.PositiveIntegerField(        default=0, choices=STATE, help_text='What stage is the article at?'    )    featured = models.BooleanField(        default=False,        help_text='Whether or not the article should get featured'    )class Page(Page):    article = models.ForeignKey(        'Article', on_delete=models.CASCADE, related_name='pages'    )    title = models.CharField(max_length=256)    number = models.PositiveIntegerField(default=1) # So pages are ordered    body = models.TextField(blank=True)
查看完整描述

2 回答

?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

根据我的评论,我认为除了实施完全定制的 CMS 之外,您无法实现您想要的一切 - 但如果您能够改变 UI 和数据建模要求,那么 WagtailRoutablePageMixin是一种可能实现将文章作为单个单元进行编辑的一般模式,同时在前端将其呈现为多个页面的方式。


在这种方法中,您将创建Article一个 Wagtail Page 模型,所有子页面内容都定义为该模型上的字段(或 InlinePanel 子模型)。(如果您想在编辑界面中将内容条目拆分为选项卡,请参阅自定义选项卡式界面,尽管这不支持动态添加/重新排序它们。)然后您将为每个子页面定义 URL 路由和模板文章:


from wagtail.core.models import Page

from wagtail.contrib.routable_page.models import RoutablePageMixin, route



class ArticlePage(RoutablePageMixin, Page):

    intro = StreamField(...)

    main_page = StreamField(...)

    conclusion = StreamField(...)


    @route(r'^$')

    def intro_view(self, request):

        render(request, 'article/intro.html', {

            'page': self,

        })


    @route(r'^main/$')

    def main_page_view(self, request):

        render(request, 'article/main_page.html', {

            'page': self,

        })


    @route(r'^conclusion/$')

    def conclusion_view(self, request):

        render(request, 'article/conclusion.html', {

            'page': self,

        })

在此示例中,三个子页面是硬编码的,但通过更多工作(可能是带有 slug 字段和 StreamField 的 InlinePanel 子模型),您可以使子页面动态化。


查看完整回答
反对 回复 2022-06-14
?
猛跑小猪

TA贡献1858条经验 获得超8个赞

我看到 gasman 已经为你的问题提供了答案,但我仍然要写一个答案,原因有两个:

  • 我认为您需要更多关于为什么gasmans 的建议比您的建议更好的解决方案的指示,但在评论中写下它的方式很多。

  • 我之前已经实现了一个类似的解决方案,其中有一个类似“文章”的顶级对象,其中包含多个可重新排序的子对象,实际内容所在的位置。

为什么你应该创建Article一个 Page 子类

您选择不创建Article的子类Page,并且您说这是因为它Article本身不包含任何内容,仅包含有关文章的元数据。这不是一个非常奇怪的思考过程,但我认为您正在查看Article模型的错误要求。

让我们看看 Wagtail 自己的Page模型。它提供了哪些开箱即用的功能?

  • 它提供了带有父页面和子页面的树结构,因此您的页面可以放置在网站层次结构中的某个位置

  • 它提供了一个slug_field, 以便 Wagtail 可以自动处理链接到您的页面。

  • 它提供了起草、发布和取消发布的功能。

Wagtail 对内容没有任何规定,让您决定要放在Page子类中的内容类型(如果有的话)。没有正文的页面示例如下:

  • 联系表格。

  • 博客索引页面。

在决定是否希望 aModel成为 a 的子类时,您可以提出的好问题Page是:

  • 我希望这个对象有它自己的 url 吗?

  • 我是否希望能够将此对象放置在我的网站层次结构中的某个位置?

  • 我想对对象有 SEO 优势吗?

  • 我是否希望能够发布/取消发布此对象?

在您的情况下Article,您几乎可以对所有这些问题说“是”,因此将其设为Page子类是明智的。这样,您就不必重新发明轮子。

如何定义页面的实际“正文”取决于您。您可以将实际内容放在该文章的片段或子页面中。或者你甚至可以选择在你的模型中创建一个 StreamFields 列表。

如何实现有序子内容。

我以前实现过这样的结构。我这样做的方式与 gasman 的建议非常相似。

就我而言,我需要创建一个网站,您可以在其中找到一个对象(如您的文章)并为其显示不同类型的解释模块。对于每个文档,我创建了一个ArticlePage,对于每个解释模块,我创建了一个名为ExplanationModule.

然后,我创建了一个带有排序的直通模型,并像 gasman 解释的那样在类中添加了一个 RoutablePageMixin。

结构看起来像这样:

@register_snippet

class ArticlePageModule(models.Model):

    ...


    title = models.CharField(max_length=100)

    body = StreamField(LAYOUT_STREAMBLOCKS, null=True, blank=True)


    panels = [

        FieldPanel('title'),

        StreamFieldPanel('body'),

    ]


class ArticlePageModulePlacement(Orderable, models.Model):

    page = ParentalKey('articles.ArticlePage', on_delete=models.CASCADE, related_name='article_module_placements')


    article_module = models.ForeignKey(ArticlePageModule, on_delete=models.CASCADE, related_name='+')


    slug = models.SlugField()


    panels = [

        FieldPanel('slug'),

        SnippetChooserPanel('article_module'),

    ]


class ArticlePage(Page, RoutablePageMixin):

    # Metadata and other member values

    ....


    content_panels = [

    ...

    InlinePanel('article_module_placements', label="Modules"),

    ]


    @route(r'^module/(?P<slug>[\w\-]+)/$')

    def page_with_module(self, request, slug=None):

        self.article_module_slug = slug

        return self.serve(request)



    def get_context(self, request):

        context = super().get_context(request)


        if hasattr(self, 'article_module_slug'):

            context['ArticlePageModule'] = self.article_module_placements.filter(slug = self.article_module).first().article_module


        return context

它的作用如下:

  • 创建一个 ArticlePageModule 片段,它只是某种内容,例如标题和正文。

  • 创建一个将 ArticlePage 链接到模块的 ArticlePageModulePlacement,并添加以下内容:

    • 蛞蝓

    • 一个 Ordering(因为它是 Orderable 混合的子类)

  • 创建一个做两件事的 ArticlePage:

    • 定义一个 ArticlePageModulePlacement 面板,它允许您添加 ArticlePageModulePlacements

    • 子类 RoutablePagemixin,如 gasman 的回答中所述。

这为您提供了一种防鹡鸰、可重复使用且强大的方式来创建带有子内容的文章。模块不会显示在选项卡中,但会显示在名为“模块”的面板下的页面布局页面上。


查看完整回答
反对 回复 2022-06-14
  • 2 回答
  • 0 关注
  • 153 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号