Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#32011 closed Bug (invalid)

Error when calling add() on ManyRelatedManager with intermediary model

Reported by: Philipp Maino Owned by: nobody
Component: Documentation Version: 2.1
Severity: Normal Keywords: ManyToMany, ManyRelatedManager
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Django is not managing our databases for us, therefor I created the table RulesetRuleMap to handle the ManyToMany relationship between Ruleset and Rule: Each Ruleset can consist of multiple Rules and each Rule can be used in multiple Rulesets.

Models

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'

Serializers

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

On a homepage the user can add/modify a Ruleset and add/modify the assosiated Rules. On submission we receive a payload like this:

{
  "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"
    }
  ]
}

As described in DRF I defined a custom create() for the nested RulesetSerializer to handle the creation of multiple objects. According to / Django one should be able to


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


When executing ruleset.rules.add(*rules_storage, through_defaults={}) I get the error

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

When executing ruleset.rules.add(*rules_storage) I get the error

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

Is there a mistake in my model and/or serializer set up or is there a bug in django?

Change History (2)

comment:1 by Mariusz Felisiak, 4 years ago

Resolution: invalid
Status: newclosed

through_defaults was added in Django 2.2, please use documentation for the version you're using.

in reply to:  1 comment:2 by Philipp Maino, 4 years ago

Replying to felixxm:

through_defaults was added in Django 2.2, please use documentation for the version you're using.

Hi @felixxm, I'm very happy if that's the problem at hand. I scaned over the docs for 2.1 and didn't this difference:

3.1
You can also use add(), create(), or set() to create relationships, as long as you specify through_defaults for any required fields

2.1
Unlike normal many-to-many fields, you can’t use add(), create(), or set() to create relationships:

Note: See TracTickets for help on using tickets.
Back to Top