Django: Custom Save method for Many-to-Many relati

2020-07-27 02:10发布

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

2条回答
乱世女痞
2楼-- · 2020-07-27 02:34

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.

查看更多
我欲成王,谁敢阻挡
3楼-- · 2020-07-27 02:49

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.

查看更多
登录 后发表回答