Django模型菱形多重继承问题:save方法重复调用如何解决?
解决Django多继承模型save方法重复调用的问题
嘿,这个问题其实是两个因素共同导致的:每个子类里重复调用了super().save(),加上Django多继承的方法解析顺序(MRO)的连锁效应,才出现了调用次数翻倍的情况。咱们一步步来解决:
问题根源分析
- 重复调用
super.save():你每个子类(FaceModel/ObjectModel/PlaceModel)的save方法里都调用了两次super().save()——一次带参数,一次不带。这本身就会让父类的save方法被触发两次,再加上多继承的传递,后面的类调用次数自然指数级增长。 - 多继承MRO的连锁触发:
AllModel的方法解析顺序是AllModel → FaceModel → ObjectModel → PlaceModel → ImageModel → models.Model。当FaceModel调用super().save()时,会触发ObjectModel的save;ObjectModel调用super()又会触发PlaceModel的save,以此类推。叠加每个子类自己的两次调用,就出现了FaceModel1次、ObjectModel2次、PlaceModel4次的情况。
解决方案一:移除重复的super.save()调用
最简单的修复就是去掉每个子类里第二次无参数的super().save()调用,只保留一次带参数的父类调用。这样每个子类的save只会触发父类一次,沿着MRO走下来每个模型的save就只会被调用一次:
class ImageModel(models.Model): pass class FaceModel(ImageModel): def save(self, *args, **kwargs): print "FaceModel" super(FaceModel, self).save(*args, **kwargs) # Do Something # 这里的逻辑只执行一次 class ObjectModel(ImageModel): def save(self, *args, **kwargs): print "ObjectModel" super(ObjectModel, self).save(*args, **kwargs) # Do Something class PlaceModel(ImageModel): def save(self, *args, **kwargs): print "PlaceModel" super(PlaceModel, self).save(*args, **kwargs) # Do Something class AllModel(FaceModel, ObjectModel, PlaceModel): pass
这样修改后,当你保存AllModel实例时,每个父类的save方法都会被调用一次,输出顺序会是FaceModel → ObjectModel → PlaceModel,符合预期且没有重复。
解决方案二:改用Django信号(更优雅的多继承场景方案)
如果你的业务逻辑是需要在模型保存后执行(也就是# Do Something的部分),推荐使用Django的post_save信号来替代重写save方法。这种方式完全避开了多继承带来的方法调用顺序问题,每个模型的逻辑只会在保存完成后触发一次:
from django.db.models.signals import post_save from django.dispatch import receiver class ImageModel(models.Model): pass class FaceModel(ImageModel): pass class ObjectModel(ImageModel): pass class PlaceModel(ImageModel): pass class AllModel(FaceModel, ObjectModel, PlaceModel): pass # 为FaceModel添加保存后信号 @receiver(post_save, sender=FaceModel) def handle_face_save(sender, instance, created, **kwargs): print "FaceModel post save" # Do Something # 为ObjectModel添加保存后信号 @receiver(post_save, sender=ObjectModel) def handle_object_save(sender, instance, created, **kwargs): print "ObjectModel post save" # Do Something # 为PlaceModel添加保存后信号 @receiver(post_save, sender=PlaceModel) def handle_place_save(sender, instance, created, **kwargs): print "PlaceModel post save" # Do Something
信号的优势在于,不管继承关系多么复杂,只要实例属于对应的模型(AllModel继承了三个父类,所以保存时三个信号都会触发),每个信号接收器只会执行一次,完美解决重复调用的问题。
内容的提问来源于stack exchange,提问作者kepler92
相关产品推荐
相关产品推荐

