Opened 2 years ago

Closed 2 years ago

Last modified 2 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

Description

Hello,

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([t.name 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.name, self.tags_()))
            f.close

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:


Album_1



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


Album_2
tag1


Let's remove both tags and save:


Album_2
tag1, tag2


Let's save it again:


Album_2


So, while everything looks good in Django admin, we get old values of album.tags_() method in Album.save(), 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.name, instance.tags_()))
        f.close

And even like this:

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

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

Change History (2)

comment:1 Changed 2 years ago by russellm

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to invalid
  • Status changed from new to closed

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