通过 wagtail FieldPanel 中的字段处理 M2M 的最简单方法是什么?

What is the simplest way to handle M2M through fields in wagtail FieldPanel?

我最近添加了一个 "through" 模型来允许对连接的对象进行排序。 在下面的示例中,舞台具有通过 StageBlock 链接的有序块列表(具有 StageBlock.order 字段)

@register_snippet
class Block(index.Indexed, models.Model):
    title = models.CharField(max_length=100, verbose_name=_("Block Name"))

@register_snippet
class Stage(index.Indexed, models.Model):
    title = models.CharField(max_length=100, verbose_name=_("Stage Name"))
    blocks = models.ManyToManyField(
        to="app.Block",
        blank=True,
        help_text=_("Blocks associated to this stage"),
        related_name="stages",
        verbose_name=_("Blocks"),
        through="StageBlock",
    )

    panels = [
        FieldPanel("title", classname="title full"),
        FieldPanel(
           "blocks",
           widget=autocomplete.ModelSelect2Multiple(
               url="block-autocomplete",
               attrs={"data-maximum-selection-length": 3},
           ),
        ),



class StageBlock(models.Model):
    block = models.ForeignKey("app.Block", on_delete=models.CASCADE)
    stage = models.ForeignKey("app.Stage", on_delete=models.CASCADE)
    order = models.PositiveSmallIntegerField()

问题是相关的 Wagtail 管理表单损坏了,因为它试图将 Block 对象关联到 Stage,而不提供 "through" 模型 "order" 字段值。

我想知道什么是 cleanest/least 允许在管理面板中按顺序选择元素的努力解决方案,然后正确保存 Stage 实例及其块和相关的阶段块。

目前,我将向代码段添加自定义表单,并根据表单数据中块的位置自动分配顺序(希望它始终与字段面板中选择的块顺序相匹配) .

感觉这个用例可以通过 wagtail-autocomplete 插件或 wagtail fieldpanel 自动处理。 但据我了解,fieldpanel 将简单地重新使用 Django ModelMultipleChoiceField field, which returns a html 元素。

与 'through' 模型的多对多关系在结构上与 'through' 模型上的一对多子关系相同,因此一种可能性是用一个 InlinePanel (as described here):

from django_modelcluster.fields import ParentalKey
from django_modelcluster.models import ClusterableModel

from wagtail.core.models import Orderable


@register_snippet
class Stage(index.Indexed, ClusterableModel):
    title = models.CharField(max_length=100, verbose_name=_("Stage Name"))

    panels = [
        FieldPanel("title", classname="title full"),
        InlinePanel("blocks", label="Blocks"),
    ]


class StageBlock(Orderable):
    stage = ParentalKey("app.Stage", on_delete=models.CASCADE, related_name='blocks')
    block = models.ForeignKey("app.Block", on_delete=models.CASCADE)

    panels = [
        FieldPanel('block'),
    ]