pythondjangodjango-modelsmanytomanyfield

Adding an element to a ManyToManyField in Django not saved?


While I was making one of my first projects on Django, I had to create two models. One of them, Image, has a field tags which is a ManyToManyField referring to the other model, Tag. I wanted to add elements to the tags field with a post_save signal. No problem occur during this process (in particular, the elements I want to add exist), but at the end no element is added.

Here is a snipplet of the models.py I've written so far :

# (Importing the modules ...)

class Image(models.Model):
    id = models.AutoField(primary_key=True)
    image = models.ImageField(upload_to="media/")
    tags = models.ManyToManyField("Tag", related_name="images", blank=True)

class Tag(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    def __str__(self):
        return self.name

@receiver(post_save, sender=Image)
def create_tags(sender, instance, created, **kwargs):
    if created:
        print(f"Before : {instance.tags.all()}")
        tag = Tag.objects.get(name="mytag")
        instance.tags.add(tag)
        instance.save()
        print(f"After : {instance.tags.all()}")

When I add an element through the admin panel, the following is printed :

Before : <QuerySet []>
After : <QuerySet [<Tag: mytag>]>

But if I check the freshly added element, no tag is selected.

Could you please help me figuring out what I am doing wrong ? Thanks in advance and have a nice day !


EDIT : I have added the following at the end of create_tags:

print(f"Instances with tag 'mytag' : {Tag.objects.get(name='mytag').images.all()}")

Every time I add an element, this only displays the element I am currently adding. Would this mean that the modification is not carried out of create_tags ?


EDIT 2 : I finally figured out where the problem was. See my answer.


Solution

  • I've finally found the problem ! At least for ManyToManyField, tt turns out that adding an element should be done using a transaction. For this, one has to import from django.db import transaction. Then, instead of just writing instance.tags.add(tag), this operation should be enveloped as follows :

    def add_tag():
        instance.tags.add(tag)
    transaction.on_commit(add_tag)
    

    If someone encounters the same problem, I hope this might help :)