Django: accessing ManyToManyField objects after th

2019-04-02 00:52发布

问题:

This is baffling me... When I save my model, the book objects are unchanged. But if I open the invoice and save it again, the changes are made. What am I doing wrong?

class Invoice(models.Model):
    ...
    books = models.ManyToManyField(Book,blank=True,null=True)
    ...

    def save(self, *args, **kwargs):
        super(Invoice, self).save(*args, **kwargs)
        for book in self.books.all():
            book.quantity -= 1
            if book.quantity == 0:
                book.sold = True;
            book.save()

Edit: I've tried using the post_save signal, but it works the same way. No changes on the first save, changes saved the second time.

Update: Seems to be solved with this code:

class InvoiceAdmin(admin.ModelAdmin):
    ...

    def save_model(self, request, obj, form, change):
        obj.save()
        for bk in form.cleaned_data['books']:
            book = Book.objects.get(pk=bk.id)
            book.quantity -= 1
            if book.quantity == 0:
                book.sold = True;
            book.save()

回答1:

This is how I worked around this, indeed baffling, behavior. Connect a signal receiver to models.signals.m2m_changed event, this get's triggered each time a m2m field is changed. The inline comments explain why.

class Gig(models.Model):
    def slugify(self):
        # Add venue name, date and artists to slug
        self.slug = slugify(self.venue.name) + "-"
        self.slug += self.date.strftime("%d-%m-%Y") + "-"
        self.slug += "-".join([slugify(artist.name) for artist in self.artists.all()]) 
        self.save()


@receiver(models.signals.m2m_changed, sender=Gig.artist.through)
def gig_artists_changed(sender, instance, **kwargs):
    # This callback function get's called twice. 
    # 1 first change appears to be adding an empty list
    # 2nd change is adding the actual artists
    if instance.artist.all() and not instance.slug:                                                                                                                                                               
        instance.slugify()


回答2:

That's because m2m relation are saved after your model save, in order to obtain PK of parent object. In your case, second save works as expected because model already has PK and associated books from first save (it's done in a signal).

I haven't found the solution yet, best bet is to do your changes in admin view, i guess.