Django: ManyToManyField с ошибкой промежуточной модели

Django не управляет нашими базами данных за нас, поэтому я создал таблицу RulesetRuleMap справиться с ManyToMany отношения между Ruleset и Rule: Каждый 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

На домашней странице пользователь может добавить / изменить Ruleset и добавить / изменить связанные 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, я определил собственный create() для вложенных RulesetSerializerдля обработки создания нескольких объектов. Согласно Django, нужно уметь

используйте add(), create() или set() для создания отношений, если вы укажете through_defaults для любых обязательных полей.

При выполнении 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.

Есть ли ошибка в моей модели и / или настройке сериализатора или есть ошибка в django?

2 ответа

Решение

Я неправильно прочитал django docs. Я использую django 2.1, а сквозные поля были введены только в 2.2.

Как там сказано:

Невозможно использовать add() для ManyToManyField, который указывает промежуточную модель.

Вы указали свою собственную сквозную модель, RulesetRuleMap, поэтому вам нужно создавать объекты самостоятельно, потому что django не поддерживает add() для этого сценария.

Если вы хотите использовать add(), create() или же set() затем, согласно документации, указанной ниже, вам необходимо сдать kwarg through_defaultsдля любых обязательных полей. У вас нет дополнительных обязательных полей, поэтому потребуется отладка на вашем конкретном наборе моделей.

Документация DRF для этой связи со сквозной моделью находится здесь

В django docs есть хороший пример настройки объектов с сквозной моделью здесь

В примере есть 3 модели, Person, Group и Membership. Group имеет M2M для Person через Membership.

>>> 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>]>
Другие вопросы по тегам