Django创建MyModel实例时无法触发函数执行?多对多关系问题
解决Django Admin创建MyModel实例时触发关联Stuff对象方法的问题
先修正原有代码里的明显错误
- 你原来的
on_save方法名不对,Django模型要重写保存逻辑应该用save方法,而且循环里的变量名写错了,把many_stuff.custom_function()改成stuff.custom_function():
def save(self, *args, **kwargs): # 这个只对已存在的实例修改保存时有效,新实例第一次保存时还没多对多关联 for stuff in self.many_stuff.all(): stuff.custom_function() super(MyModel, self).save(*args, **kwargs)
- 删掉
__init__里遍历many_stuff的代码,因为新实例还没保存到数据库,没有id,根本无法访问多对多关联的数据,这就是你报错的原因。
用m2m_changed信号处理新实例的多对多关联
问题出在Django Admin创建新实例的流程:先保存MyModel主实例(此时还没关联任何Stuff),然后才会把你在Admin里选择的Stuff关联上去。所以post_save信号触发时,多对多关系还没建立,自然不会执行方法。
正确的做法是监听m2m_changed信号,这个信号会在多对多关系发生变化(比如添加、删除关联)时触发,而且能拿到新增的对象列表。
添加以下信号接收器代码(可以放在models.py里,或者单独的signals.py文件,记得在apps.py里注册):
from django.db.models.signals import m2m_changed from django.dispatch import receiver @receiver(m2m_changed, sender=MyModel.many_stuff.through) def handle_many_stuff_add(sender, instance, action, pk_set, **kwargs): # 当动作是post_add时,说明已经有Stuff被关联到当前MyModel实例 if action == 'post_add': # 遍历新增的Stuff对象的id,查询出实例并执行方法 for stuff_id in pk_set: stuff = Stuff.objects.get(id=stuff_id) stuff.custom_function()
为什么这个方法有效?
当你在Django Admin创建新MyModel实例,选择好Stuff并点击保存后,流程是:
- 保存MyModel主实例(生成id)
- 建立MyModel和选中Stuff的多对多关联
- 触发m2m_changed信号,action为
post_add,此时能拿到所有新增的Stuff的id,执行custom_function
完整代码示例
from django.db import models from django.db.models.signals import m2m_changed from django.dispatch import receiver class Stuff(models.Model): # 你的其他字段 def custom_function(self): # 你的方法逻辑 pass class MyModel(models.Model): name = models.CharField(max_length=200) many_stuff = models.ManyToManyField(Stuff, related_name="many_stuff+") many_other_stuff = models.ManyToManyField(Stuff, related_name="many_other_stuff+") def save(self, *args, **kwargs): # 针对已存在的实例修改保存时,执行关联Stuff的方法 if self.pk: # 只有实例已存在(有id)时才执行 for stuff in self.many_stuff.all(): stuff.custom_function() super(MyModel, self).save(*args, **kwargs) @receiver(m2m_changed, sender=MyModel.many_stuff.through) def handle_many_stuff_add(sender, instance, action, pk_set, **kwargs): if action == 'post_add': for stuff_id in pk_set: stuff = Stuff.objects.get(id=stuff_id) stuff.custom_function()
内容的提问来源于stack exchange,提问作者FooBarista
相关产品推荐
相关产品推荐

