Search code examples
pythondjangodjango-modelsdjango-related-manager

How to use RelatedManager add() method in save()


Banging head against the wall again. I'm trying to add tags using other known fields

# models.py
class MyModel(models.Model):
    ...
    tags = models.ManyToManyField(Tag, blank=True)
    field_m2m = models.ManyToManyField('M2mModel', blank=True)
    field_fk = models.ForeignKey('FkModel', blank=True, null=True)
    ...
    def save(self, *args, **kwargs):
        for inst in self.field_m2m.all():
            self.tags.add(Tag.objects.get(name=inst.name))
        self.tags.add(Tag.objects.get(name=self.field_fk.name))
        super(MyModel, self).save(*args, **kwargs)

class FkModel(models.Model):
    name = models.CharField(max_length=255, unique=True)
    ...

class M2mModel(models.Model):
    name = models.CharField(max_length=255, unique=True)
    ...

I am 100% sure my field_m2m and field_fk aren't empty and what's not less important: there are instances corresponding to EXISTING tags. I have other functions covering this part well. I have also tried hardcoding the strings (Tag.objects.get(name="mystring")) to be 101% sure.

Yet, no tags are assigned through admin panel. I tried to go through the steps in shell and it works there.

>>> m = MyModel.objects.get(name='something')
>>> t = Tag.objects.get(name='sometag')
>>> m.tags.add(t)
>>> m.tags.all()
[<Tag: sometag>]

How to make it work from save() method?

Also until the the model instance is created for the first time, traceback is complaining about: "<MyModel: Something>" needs to have a value for field "mymodel" before this many-to-many relationship can be used.

I guess I should save the model instance before even doing aforementioned assignments, right? How can I do it all at once?


Solution

  • Figured it out thanks to this hint: https://stackoverflow.com/a/6200451/1344854
    Model save() method stays default. First I tied a tag to my FkModel and M2mModel instances. The rest of the job is done in ModelAdmin.

    # models.py
    class FkModel(models.Model):
        name = models.CharField(max_length=255, unique=True)
        tag = models.ForeignKey(Tag, blank=True, null=True, related_name='fk')
        ...
    
    class M2mModel(models.Model):
        name = models.CharField(max_length=255, unique=True)
        tag = models.ForeignKey(Tag, blank=True, null=True, related_name='m2m')
        ...
    
    # admin.py
    class MyModelAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            form.save() # as I learned, without saving at this point, obj.field_m2m.all() will be empty
            tags = []
            if obj.field_m2m.all():
                tags += [m2m.tag for m2m in obj.field_m2m.all()]
            if obj.field_fk:
                tags += [obj.field_fk.tag]
            form.cleaned_data['tags'] = tags
            super(MyModelAdmin, self).save_model(request, obj, form, change)