You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

一对多(外键)关系下m2m_changed信号的替代方案咨询

问题

我有模型A(作为模型B的外键)和模型C。需求是:当在A的Admin内联中修改B后,获取该A关联的所有B实例的quantity总和,以此更新模型C。
具体步骤:

  1. 当保存A1的Admin表单时,B1、B2、B3均以A1作为fk_a;
  2. 等待所有B实例完成save()后,计算它们的quantity总和,并用该值更新对应的C1、C2、C3。

相关模型代码:

from django.db import models

class A(models.Model):
    name = models.CharField(max_length=20)

class B(models.Model):
    quantity = models.IntegerField()
    fk_a = models.ForeignKey(A, on_delete=models.CASCADE)
    
    def save(self):
        sum_of_similar_b = B.objects.filter(fk_a=self.fk_a)
        similar_c = C.objects.filter(fk_a=self.fk_a)
        update_model_c(sum_of_simlar_b, similar_c)

我尝试过post_save信号,但它在外键更新前就被触发;也试过重写A的save()方法,效果相同;尝试无through表的m2m_changed信号,但未触发。

解决方案

方法1:重写Admin的save_formset方法

在A的Admin类中重写save_formset,这个方法会在所有内联的B实例保存完成后执行,完全匹配需求场景:

from django.contrib import admin
from .models import A, B, C

class BInline(admin.TabularInline):
    model = B

class AAdmin(admin.ModelAdmin):
    inlines = [BInline]

    def save_formset(self, request, form, formset, change):
        # 先保存所有内联的B实例
        formset.save()
        # 获取当前操作的A实例
        a_instance = form.instance
        # 计算该A关联的所有B的quantity总和
        total_quantity = B.objects.filter(fk_a=a_instance).aggregate(total=models.Sum('quantity'))['total'] or 0
        # 批量更新对应的C实例
        C.objects.filter(fk_a=a_instance).update(total_quantity=total_quantity)

admin.site.register(A, AAdmin)

方法2:使用transaction.on_commit延迟计算

如果不想修改Admin类结构,可以在post_save信号中通过事务提交后的回调,确保所有B实例都已完成保存:

from django.db.models.signals import post_save
from django.db import transaction
from django.dispatch import receiver
from .models import B, C

@receiver(post_save, sender=B)
def update_c_after_b_save(sender, instance, **kwargs):
    def update_c_task():
        a_instance = instance.fk_a
        total_quantity = B.objects.filter(fk_a=a_instance).aggregate(total=models.Sum('quantity'))['total'] or 0
        C.objects.filter(fk_a=a_instance).update(total_quantity=total_quantity)
    # 等待当前事务提交后执行,避免计算时部分B实例未保存
    transaction.on_commit(update_c_task)

关键注意点

  • 不要在B的save()方法中直接计算总和,Admin内联保存会触发多次B的save,导致重复计算、效率低下。
  • transaction.on_commit能确保所有数据库操作完成后再执行计算,彻底解决“部分实例未保存”的问题。

内容的提问来源于stack exchange,提问作者Hazem Elmahy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 05:47:15