Django中如何从Model1的save触发Model2保存并同步字段
问题描述
现有可正常运行的Django代码:
class Model1(models.Model): name = models.CharField(max_length=50) type = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model1s" ordering = ('sioticket',) def save(self, *args, **kwargs): super(Model1, self).save(*args, **kwargs) class Model2(models.Model): name = models.ForeignKey(Model1, on_delete=models.CASCADE, null=True, blank=True) cpm = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model2s" ordering = ('sioticket',) def save(self, *args, **kwargs): vartype = self.name.type self.cpm = vartype return super(Model2, self).save(*args, **kwargs)
需求:更新Model1的type字段后,关联的Model2的cpm字段需同步更新。当前创建新Model1并关联Model2时功能正常,但更新Model1的type后,Model2的cpm不会同步。
尝试修改Model1的save方法后触发错误:
def save(self, *args, **kwargs): super(Model1, self).save(*args, **kwargs) children = self.objects.all() for child in children: if child._meta.verbose_name_plural=="Model2s": child.save()
错误信息:
Manager isn't accessible via Model1 instances
错误原因分析
self.objects.all()写法错误:objects是模型类的管理器,只能通过Model1.objects调用,不能通过Model1的实例(self)访问。要获取当前Model1实例关联的所有Model2对象,需使用Django的反向关联管理器。
解决方案
方案1:触发关联Model2的save方法
因为Model2的外键字段是name,Django默认会为Model1生成反向关联管理器model2_set,用它可获取当前实例关联的所有Model2对象:
class Model1(models.Model): name = models.CharField(max_length=50) type = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model1s" ordering = ('sioticket',) def save(self, *args, **kwargs): super(Model1, self).save(*args, **kwargs) # 获取当前Model1关联的所有Model2对象并触发save for model2_obj in self.model2_set.all(): model2_obj.save() class Model2(models.Model): name = models.ForeignKey(Model1, on_delete=models.CASCADE, null=True, blank=True) cpm = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model2s" ordering = ('sioticket',) def save(self, *args, **kwargs): if self.name: # 避免name为null时抛出异常 self.cpm = self.name.type super(Model2, self).save(*args, **kwargs)
方案2:直接批量更新Model2的cpm字段(更高效)
如果不需要触发Model2的其他save逻辑,直接用批量更新可减少数据库操作:
class Model1(models.Model): name = models.CharField(max_length=50) type = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model1s" ordering = ('sioticket',) def save(self, *args, **kwargs): super(Model1, self).save(*args, **kwargs) # 批量更新关联Model2的cpm字段 self.model2_set.update(cpm=self.type) class Model2(models.Model): name = models.ForeignKey(Model1, on_delete=models.CASCADE, null=True, blank=True) cpm = models.CharField(max_length=50) class Meta: verbose_name_plural = "Model2s" ordering = ('sioticket',) def save(self, *args, **kwargs): if self.name: self.cpm = self.name.type super(Model2, self).save(*args, **kwargs)
优化建议
若觉得model2_set名称不够直观,可给Model2的外键字段设置related_name:
class Model2(models.Model): name = models.ForeignKey(Model1, on_delete=models.CASCADE, null=True, blank=True, related_name='model2s') # 其他字段保持不变
之后在Model1里就可以用self.model2s.all()或self.model2s.update(...)操作关联的Model2对象。
内容的提问来源于stack exchange,提问作者Manuel Sánchez
相关产品推荐
相关产品推荐

