I have a model relationship as defined by the examples in the Django docs on ManyToMany relationship with an intermediary model.
I know how this normally works, but this is a quick little app that only uses the Django Admin and this is causing a slight bump in the road.
Here is what I have:
class Item(models.Model):
name = models.CharField(blank=True, max_length=100)
vat_deductable = models.BooleanField(default=True)
price = models.DecimalField(max_digits=12, decimal_places=2)
def __unicode__(self):
return self.name
class Invoice(models.Model):
customer = models.ForeignKey(Relation)
date = models.DateField(default=datetime.datetime.today)
amount = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
vat_amount = models.DecimalField(max_digits=12, decimal_places=2, blank=True, null=True)
is_paid = models.BooleanField(default=False)
paid_on = models.DateField(blank=True, null=True)
items = models.ManyToManyField(Item, through='SoldItem')
def __unicode__(self):
return unicode(self.customer) + u'_' + unicode(self.date)
class SoldItem(models.Model):
item = models.ForeignKey(Item)
invoice = models.ForeignKey(Invoice)
qty = models.IntegerField(default=1, null=True)
Here is what I would like to do:
After all models have been saved (the new instance of the parent model, any new instances of related models and any new instances of intermediary models) I would like to loop over all instances of Item
that is associated with the newly saved Invoice so I can add their price and maybe VAT to the two fields Invoice.amount
and Invoice.vat_amount
How would I do that? I have tried with custom save methods on both the Invoice
model and its ModelAdmin form, but neither place gives the complete picture when new relations are being formed.
Maybe a signal? But which?
EDIT: I have tried this solution: https://stackoverflow.com/a/2109177/150033
It would make sense that save_m2m would make sure everything is saved, but it seems that the newest relationship is always missing when trying this.
Ok, after much research I found someone else that solved this http://igorsobreira.com/blog/2011/2/12/change-object-after-saving-all-inlines-in-django-admin/
In the invoice admin:
class InvoiceAdmin(admin.ModelAdmin):
inlines = [
SoldItemAdmin,
]
def response_add(self, request, new_object):
obj = self.after_saving_model_and_related_inlines(new_object)
return super(InvoiceAdmin, self).response_add(request, obj)
def response_change(self, request, obj):
obj = self.after_saving_model_and_related_inlines(obj)
return super(InvoiceAdmin, self).response_change(request, obj)
def after_saving_model_and_related_inlines(self, obj):
solditem_changed.send(obj)
return obj
And our signal:
solditem_changed = Signal()
@receiver(solditem_changed)
def update_invoice(sender, **kwargs):
if hasattr(sender, 'solditem_set'):
total = 0
for item in sender.solditem_set.all():
total += item.item.price * item.qty
sender.amount = total
sender.save()
else:
sender.amount = 0
sender.save()