代码之家  ›  专栏  ›  技术社区  ›  Sheena

Wagtail面板,用于通过模型的自引用多对多关系

  •  0
  • Sheena  · 技术社区  · 4 年前

    我正忙着在wagtail中制作一些类似知识图的东西。

    CurriculumContentItem 是该图上的一个节点。它与自身有着多对多的关系,贯穿模型具有重要的领域。

    我正在努力让它在管理页面中可用。请查看内联评论:

    class ContentItemOrder(models.Model):
        post = models.ForeignKey(
            "CurriculumContentItem", on_delete=models.PROTECT, related_name="pre_ordered_content"
        )
        pre = models.ForeignKey(
            "CurriculumContentItem", on_delete=models.PROTECT, related_name="post_ordered_content"
        )
        hard_requirement = models.BooleanField(default=True)
    
    class CurriculumContentItem(Page):
        body = RichTextField(blank=True)
    
        prerequisites = models.ManyToManyField(
            "CurriculumContentItem",
            related_name="unlocks",
            through="ContentItemOrder",
            symmetrical=False,
        )
    
        content_panels = Page.content_panels + [
            # FieldPanel("prerequisites") 
            # FieldPanel just lets me select CurriculumContentItems, but I need to access fields in the through model
    
            # InlinePanel("prerequisites"), 
            # This causes a recursion error
    
            FieldPanel('body', classname="full collapsible"),
        ]
    

    如果我想在普通的Django管理中这样做,我会使用内联来指定先决条件。类似于:

    class ContentItemOrderPostAdmin(admin.TabularInline):
        model = models.ContentItem.prerequisites.through
        fk_name = "post"
    
    class ContentItemOrderPreAdmin(admin.TabularInline):
        model = models.ContentItem.unlocks.through
        fk_name = "pre"
    

    Wagtail有类似的机制吗?

    看起来我需要为此创建一个自定义面板。

    0 回复  |  直到 4 年前
        1
  •  0
  •   gasman    4 年前

    我建议构建一个指向“直通”模型的InlinePanel,这意味着你使用的是一对多关系,而不是多对多关系:

    class ContentItemOrder(models.Model):
        post = ParentalKey(
            "CurriculumContentItem", related_name="pre_ordered_content"
        )
        pre = models.ForeignKey(
            "CurriculumContentItem", on_delete=models.PROTECT, related_name="post_ordered_content"
        )
        hard_requirement = models.BooleanField(default=True)
    
        panels = [
            PageChooserPanel('pre'),
            FieldPanel('hard_requirement')
        ]
    
    
    class CurriculumContentItem(Page):
        body = RichTextField(blank=True)
    
        content_panels = Page.content_panels + [
            InlinePanel("pre_ordered_content"), 
    
            FieldPanel('body', classname="full collapsible"),
        ]
    
        2
  •  0
  •   Sheena    4 年前

    这样可以:

    1. 使直通模型从Orderable继承
    2. 利用 ParentalKey 而不是 ForeignKey
    3. 使用InlinePanel引用直通模型中字段的相关名称
    from modelcluster.fields import ParentalKey
    from wagtail.core.models import Page, Orderable
    
    
    class ContentItemOrder(Orderable):   ### 1
        post = ParentalKey(   ### 2
            "CurriculumContentItem", on_delete=models.PROTECT, related_name="pre_ordered_content"
        )
        pre = ParentalKey(   ### 2
            "CurriculumContentItem", on_delete=models.PROTECT, related_name="post_ordered_content"
        )
        hard_requirement = models.BooleanField(default=True)
    
        panels = [
            PageChooserPanel('pre'),
            PageChooserPanel('post'),
            FieldPanel('hard_requirement'),
        ]
    
    
    class CurriculumContentItem(Page):
        body = RichTextField(blank=True)
    
        prerequisites = models.ManyToManyField(
            "CurriculumContentItem",
            related_name="unlocks",
            through="ContentItemOrder",
            symmetrical=False,
        )
    
        content_panels = Page.content_panels + [
            InlinePanel('pre_ordered_content', label="prerequisites"),  ### 3
            InlinePanel('post_ordered_content', label="unlocks"),       ### 3
    
            FieldPanel('body', classname="full collapsible"),
        ]
    

    我担心每个内联会有2个PageChooser字段可用,但wagtail足够聪明(神奇),它只画出了我们需要的一个