Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#19731 closed Uncategorized (invalid)

previous values of ManyToManyField in model's save method

Reported by: s4mmael@… Owned by: nobody
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords: manytomanyfield, save, previous
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no



I'm terribly sorry for bothering you, but I believe this behavior may be incorrect. I've already asked at django-users mailing list and I've had no answer.

How to reproduce it:

    class Tag(models.Model):
        name = models.CharField(max_length=50, unique=True, db_index=True)
        parent = ForeignKey('self', null=True, blank=True, related_name='children')

    class Album(models.Model):
        name = models.CharField(max_length=64, blank=True)
        tags = models.ManyToManyField(Tag, blank=True)
        def tags_(self):
            return ', '.join([ for t in self.tags.all()])
        def save(self, *args, **kwargs):
            super(Album, self).save(*args, **kwargs)
            f = open('/tmp/test.txt', 'wt')
            f.write('%s\n%s\n' % (, self.tags_()))

Now let's go to django admin and create two tags: tag1, tag2. After that let's create an album 'Album_1' with tag 'tag1' and save the album. In the file /tmp/test.txt we see:


Let's add tag2 to the album, change it's name to Album_2 and save. The file will be:


Let's remove both tags and save:

tag1, tag2

Let's save it again:


So, while everything looks good in Django admin, we get old values of album.tags_() method in, even if we call super(Album, self).save(*args, * *kwargs) before.

I've also tried to use signals like this:

    @receiver(post_save, sender=Album)
    def album_save_handler(sender, instance, **kwargs):
        f = open('/tmp/test.txt', 'wt')
        f.write('%s\n%s\n' % (, instance.tags_()))

And even like this:

    @receiver(post_save, sender=Album)
    def album_save_handler(sender, instance, **kwargs):
        t = Album.objects.get(pk =
        f = open('/tmp/test.txt', 'wt')
        f.write('%s\n%s\n' % (, t))

Still no luck, results are the same: previous tags instead of the current ones.

Change History (2)

comment:1 Changed 6 years ago by Russell Keith-Magee

Resolution: invalid
Status: newclosed

You've been caught by a leaky abstraction. M2M relationships aren't saved as part of the save() method.

In the admin, the main object is saved, and then the m2m relation is saved; so, by serializing the list of tags in the save method, you're printing the value of the tags before the new values have been saved.

If you want to install "post m2m save" behavior, you'd need to override the update view on the admin itself.

comment:2 Changed 6 years ago by anonymous

Russellm, thank you very much for your response! Since I'd read your explanation it was quite simple to find the solution: the 'm2m_changed' signal do the trick.

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