首页 > 解决方案 > Django模型中的ManyToManyField

问题描述

我需要创建一组这样的模型:

class Step(models.Model):
    field1 = models.CharField(max_length=50)
    field2 = models.CharField(max_length=50)

class Scenario(models.Model):
    name = models.CharField(max_length=50)
    steps = models.ManyToManyField(Step, related_name="scenarios")

问题是我希望该场景按照我将指定的顺序包含几个步骤,甚至多次包含相同的步骤。像这样:

Scenario1:
    step1
    step2
    step1
    step3

我希望此步骤的顺序可以在管理站点中轻松编辑。我发现了filter_horizontal,它看起来很像我在管理站点中需要的东西,但是它没有选择再添加一个步骤并上下移动步骤。

标签: djangodjango-modelsdjango-formsdjango-admin

解决方案


计算机科学中有句谚语说:“计算机科学中的所有问题都可以通过另一个层次的间接性来解决”——大卫·惠勒。

在这种情况下,您应该使用“通过”表,例如:

class Step(models.Model):
    field1 = models.CharField(max_length=50)
    field2 = models.CharField(max_length=50)

class Scenario(models.Model):
    name = models.CharField(max_length=50)
    steps = models.ManyToManyField(
        Step,
        through='ScenarioStep',
        related_name="scenarios"
    )

class ScenarioStep(models.Model):
    scenario = models.ForeignKey(
        'Scenario',
        on_delete=models.CASCADE,
        related_name='scenariostep'
    )
    step = models.ForeignKey(
        'Step'
        on_delete=models.CASCADE,
        related_name='scenariostep'
    )
    order = models.PositiveIntegerField()

    class Meta:
        ordering = ['order']

因此,我们在这里引入了一个额外的模型,它替换了已经由ManyToManyField.

AScenario因此有一个Step,假设有一个ScenarioStep记录,我们通过一个Order字段对其进行排序。

例如,我们可以将给定的步骤添加到scenariowith:

s1 = Step.objects.create(field1='step1')
s2 = Step.objects.create(field1='step1')
s3 = Step.objects.create(field1='step1')

sc1 = Scenario.objects.create(name='Scenario1')

ScenarioStep.objects.create(order=1, scenario=sc1, step=s1)
ScenarioStep.objects.create(order=2, scenario=sc1, step=s2)
ScenarioStep.objects.create(order=3, scenario=sc1, step=s1)
ScenarioStep.objects.create(order=4, scenario=sc1, step=s3)

然后我们可以迭代这些步骤:

for step in sc1.steps.order_by('scenariostep'):
    # ... (do something with the step)
    pass

推荐阅读