Django:带有中间模型错误的ManyToManyField

Django: ManyToManyField with intermediary model error

Django 不为我们管理数据库,因此我创建了 table RulesetRuleMap 来处理 RulesetRule 之间的 ManyToMany 关系: 每个 Ruleset 可以由多个 Rules 组成,每个 Rule 可以在多个 Rulesets.

中使用

型号

class Rule(models.Model):
    id = models.BigAutoField(primary_key=True)
    percentage_of_total = models.FloatField(blank=False, null=False)
    _rule_parameter = models.ForeignKey('RuleParameter', models.DO_NOTHING, blank=False, null=False)

    class Meta:
        managed = False
        db_table = '_rule'


class Ruleset(models.Model):
    id = models.BigAutoField(primary_key=True)
    name = models.CharField(max_length=300, blank=False, null=False)
    description = models.CharField(max_length=300, blank=False, null=False)
    rules = models.ManyToManyField('Rule', through="RulesetRuleMap")

    class Meta:
        managed = False
        db_table = '_ruleset'


class RulesetRuleMap(models.Model):
    id = models.BigAutoField(primary_key=True)
    _rule = models.ForeignKey('Rule', models.CASCADE)
    _ruleset = models.ForeignKey('Ruleset', models.CASCADE)

    class Meta:
        managed = False
        db_table = '_ruleset_rule_map'

序列化器

class RulesetRuleMapSerializer(serializers.ModelSerializer):
    class Meta:
        model = db_models.RulesetRuleMap
        fields = '__all__'


class RuleSerializer(serializers.ModelSerializer):
    class Meta:
        model = db_models.Rule
        fields = '__all__'


class RulesetSerializer(serializers.ModelSerializer):
    rules = RuleSerializer(many=True)
    class Meta:
        model = db_models.Ruleset
        fields = '__all__'

    def create(self, validated_data):
        rules_data = validated_data.pop('rules')
        ruleset = db_models.Ruleset.objects.create(**validated_data)
        rules_storage =[]
        for rule_data in rules_data:
            rule, created = db_models.Rule.objects.get_or_create(**rule_data)
            rules_storage.append(rule)
        ruleset.rules.add(*rules_storage, through_defaults={})
        return ruleset

在主页上,用户可以 add/modify Ruleset 和 add/modify 关联 Rules。提交后,我们收到这样的有效负载:

{
  "id": None,
  "name": "Split_50.0_Param1_50.0_Param2",
  "description": "test",
  "rules": [
    {
      "id": None,
      "percentage_of_total": "50",
      "tc_rule_parameter": "3"
    },
    {
      "id": None,
      "percentage_of_total": "50",
      "tc_rule_parameter": "2"
    }
  ]
}

Djange REST Framework I defined a custom create() for the nested RulesetSerializer to handle the creation of multiple objects. According to Django中所述,应该可以

use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields.

执行ruleset.rules.add(*rules_storage, through_defaults={})时出现错误

{TypeError}add() got an unexpected keyword argument 'through_defaults'

执行ruleset.rules.add(*rules_storage)时出现错误

{AttributeError}Cannot use add() on a ManyToManyField which specifies an intermediary model.Use database_models.TcRulesetRuleMap's Manager instead.

我的模型 and/or 序列化程序设置有误还是 django 中有错误?

正如那里所说:

Cannot use add() on a ManyToManyField which specifies an intermediary model.

您已经通过模型指定了自己的对象,RulesetRuleMap,因此您需要自己创建对象,因为 django 不支持 add() 这种情况。

如果您想使用 add()create()set(),那么根据下面列出的文档,您需要为任何需要的内容传递 kwarg through_defaults领域。您没有任何额外的必填字段,因此需要对您的特定模型集进行调试。

这种与直通模型的关系的 DRF 文档是 here

django 文档中有一个很好的示例,用于使用直通模型设置对象 here

示例中有3个模型,PersonGroupMembershipGroup 通过 MembershipPerson 的 M2M。

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

我误读了 django 文档。我在 django 2.1 上,through 字段只在 2.2 中引入。