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

#django #django-rest-framework #many-to-many #django-intermediate-table

#django #django-rest-framework #многие ко многим #django-промежуточная таблица

Вопрос:

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?

Ответ №1:

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

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

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

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

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

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

В примере есть 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>]>
  

Комментарии:

1. Пожалуйста, объясните разницу между моделями django и моей @markwalker_ . В связанном примере django также указана его собственная сквозная модель, и в нем можно использовать add(), create() или set(). Набор правил -> Членство, Правило -> Лицо, Набор правил -> Группа

2. На самом деле разницы в моделях нет. Они определяют две модели, которые связаны с M2M через пользовательскую модель, а не с волшебной моделью django M2M, как и вы. Rule похоже Person , Ruleset похоже Group и RulesetRuleMap похоже Membership . Вы правы — документы также показывают, как вы можете использовать некоторые встроенные методы, такие как add() , и у вас нет дополнительных обязательных полей, поэтому непонятно, почему у django проблема с тем, что вы делаете, без дополнительной отладки вашего конкретного сценария

Ответ №2:

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