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>]>