I need to set custom save and delete methods on a Many-to-Many relation.
I tried specifying a model with the "through" attribute but this over-complicated my code and introduced some problems. I don't need any extra field on the Many-to-Many model, just custom save and delete methods.
Is it possible to accomplish this without specifying the "through" attribute?
Here's code:
class Order(BaseDate):
#lots of fields
relateds = models.ManyToManyField('RelatedProduct', verbose_name=_('related products'), blank=True, related_name='order_relateds', through='OrderRelateds')
# more fields
total = CurrencyField(verbose_name=_('total'))
def calculate_total(self):
cleanses = self.cleanse.taxed_price() * self.quantity
delivery = DELIVERY_PRICE if self.delivery == 'delivery' else 0
relateds = 0
for r in self.relateds.all():
relateds = relateds + float(r.taxed_price())
total = float(cleanses) + delivery + relateds
return total
def save(self, *args, **kwargs):
self.total = '%.2f' % self.calculate_total()
super(Order, self).save(*args, **kwargs)
class OrderRelateds(models.Model):
order = models.ForeignKey(Order)
relatedproduct = models.ForeignKey(RelatedProduct, verbose_name=_('related product'))
class Meta:
verbose_name = _('Related Product')
verbose_name_plural = _('Products Related to this Order')
def __unicode__(self):
return self.relatedproduct.__unicode__()
def save(self, *args, **kwargs):
super(OrderRelateds, self).save(*args, **kwargs)
self.order.save()
def delete(self, *args, **kwargs):
super(OrderRelateds, self).delete(*args, **kwargs)
self.order.save()
I need to trigger recalculation of total price of the order if any related product (many to many item) is added to or removed from an order.
Edit: this is the code which solved my problem
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
@receiver(m2m_changed, sender=Order.relateds.through)
def recalculate_total(sender, instance, action, **kwargs):
"""
Automatically recalculate total price of an order when a related product is added or removed
"""
if action == 'post_add':
instance.save()
if action == 'post_remove' or action == 'post_clear':
instance.save()
You could make use of django's
m2m_changed
,pre_save/post_save
,pre_delete/post_delete signals
on the Model containing the relation, and do the related logic there.Timmy's answer is definitely correct and works. For your special case however, I am wondering, if you should not handle that logic in the view, where you manage the order and order items and enforce the recalculation there, since the adding, editing and deleting of order items all belong together and belong to the order.