如何将Django模型字段默认值设为关联外键实例的字段值?
首先,咱们来搞清楚为什么会报这个错:当你在B模型里写b_bool = models.BooleanField(default=link.my_bool)时,这里的link是Django的ForeignKey类对象,而不是实际关联的A模型实例——在模型定义阶段,还没有任何数据库记录,所以link根本没有my_bool这个属性,自然会触发AttributeError。
模型字段的default参数只能用静态值、无参可调用对象,或者能在模型加载阶段就确定的值,没法直接引用关联模型的实例属性。接下来给你几个适配你用Graphene Mutation创建实例场景的解决方案:
方案1:在Graphene Mutation里直接处理逻辑
既然你是通过Mutation创建B实例,最直接的方式就是在Mutation的mutate方法里判断是否传入了b_bool,如果没传就从关联的A实例里取my_bool的值。
首先修正你的模型(记得给ForeignKey加上on_delete参数,不然迁移会报错):
# models.py class A(models.Model): name = models.CharField(max_length=100) description = models.CharField(max_length=15) my_bool = models.BooleanField(default=True) class B(models.Model): link = models.ForeignKey(A, on_delete=models.CASCADE) b_bool = models.BooleanField() # 不需要设置默认,由Mutation控制
然后写对应的Graphene Mutation:
# schema.py import graphene from graphene_django import DjangoObjectType from .models import A, B class AType(DjangoObjectType): class Meta: model = A fields = "__all__" class BType(DjangoObjectType): class Meta: model = B fields = "__all__" class CreateB(graphene.Mutation): b = graphene.Field(BType) class Arguments: link_id = graphene.Int(required=True) # 传入A的ID来关联 b_bool = graphene.Boolean() # 设为可选参数,不强制传入 def mutate(self, info, link_id, b_bool=None): # 获取关联的A实例 a_instance = A.objects.get(id=link_id) # 如果没传b_bool,就用A的my_bool值 if b_bool is None: b_bool = a_instance.my_bool # 创建并保存B实例 b_instance = B.objects.create(link=a_instance, b_bool=b_bool) return CreateB(b=b_instance) class Mutation(graphene.ObjectType): create_b = CreateB.Field() schema = graphene.Schema(mutation=Mutation)
这种方式把创建逻辑集中在Mutation层,符合GraphQL的使用习惯,也不会污染模型本身的代码。
方案2:重写B模型的save方法
如果你希望不管通过什么方式创建B实例(比如Django Admin、其他视图)都能自动设置默认值,可以重写save方法:
# models.py class B(models.Model): link = models.ForeignKey(A, on_delete=models.CASCADE) b_bool = models.BooleanField(null=True, blank=True) # 允许临时为空 def save(self, *args, **kwargs): # 只在创建新实例(还没主键)且b_bool未设置时填充默认值 if not self.pk and self.b_bool is None: self.b_bool = self.link.my_bool super().save(*args, **kwargs)
这里设置null=True, blank=True是为了允许创建时不传入b_bool,等save方法执行时再填充值。如果不想让数据库字段允许为空,可以把null=True去掉,改成default=None,逻辑一样。
方案3:使用Django信号
如果不想修改模型的save方法,也可以用pre_save信号来监听保存事件,自动设置默认值:
# models.py from django.db.models.signals import pre_save from django.dispatch import receiver class A(models.Model): # ... 你的字段 ... class B(models.Model): link = models.ForeignKey(A, on_delete=models.CASCADE) b_bool = models.BooleanField(null=True, blank=True) @receiver(pre_save, sender=B) def set_b_bool_default(sender, instance, **kwargs): if not instance.pk and instance.b_bool is None: instance.b_bool = instance.link.my_bool
这种方式的好处是解耦,模型本身不需要修改save方法,适合复杂的项目场景。
内容的提问来源于stack exchange,提问作者Oguntoye

