views:

195

answers:

1

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()
A: 

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.

Dmitry Shevchenko
I know they're saved after the model save, which is why I put the for clause after super().save() Are they saved after post_save signal? Is there a signal for when they are saved?
tufelkinder
they're saved after Invoice.save() call so even if you put after parent save it won't get m2m saved. As for signals look at m2m_saved
Dmitry Shevchenko
But I'm wondering what function is called after save to save the m2m fields? Couldn't I override that field instead?
tufelkinder
don't know about function, but overriding field might work
Dmitry Shevchenko