如何从Django关联模型的get_organization_name方法中获取值?
Django一对一模型中通过annotate获取实例方法返回值的问题
我有两个Django模型Model1和Model2,Model2与Model1为一对一关联,get_organization_name方法定义在Model2中。尝试通过annotate结合F表达式获取该方法的返回值但未成功,相关代码如下:
模型代码
class Model1(models.Model): code = models.CharField(max_length=5) random_id = models.IntegerField(primary_key=True) random_type = models.CharField(max_length=5) random_code = models.CharField(max_length=10) random_name = models.CharField(max_length=200) class Model2(models.Model): id = models.AutoField(primary_key=True) claim_id = models.PositiveIntegerField() random = models.OneToOneField('Model1', on_delete=models.DO_NOTHING, db_column='random_id', related_name='polis_journal') random_type = models.CharField(max_length=255) organization_id = models.PositiveIntegerField() def get_organization_name(self): return polis_organizations.get(self.organization_id, None)
尝试的代码
queryset = Model1.objects.all() queryset = queryset.annotate(organization_name=F('polis_journal__get_organization_name'))
问题原因及解决方案
为什么原方法失败
F表达式的作用是引用数据库中的字段,它会被转换成SQL语句的一部分在数据库层面执行。而get_organization_name是Model2的实例方法,只有当Model2对象在Python中被初始化后才能调用,数据库无法识别和执行这个Python方法,所以直接用F表达式引用会报错。
可行的解决办法
方法一:查询后逐个调用实例方法(适合小数据量)
先通过select_related提前关联Model2避免N+1查询,再遍历QuerySet调用实例方法:
queryset = Model1.objects.select_related('polis_journal').all() for model1_obj in queryset: # 注意处理polis_journal不存在的情况(一对一关联可能为空) organization_name = model1_obj.polis_journal.get_organization_name() if model1_obj.polis_journal else None
方法二:将逻辑移到数据库层面(适合大数据量批量查询)
如果polis_organizations是固定的键值对字典(比如{1: "XX机构", 2: "YY机构"}),可以用Case和When构造数据库层面的条件判断,直接在annotate中生成结果:
from django.db.models import Case, When, Value, CharField # 把字典转换成When条件列表 when_clauses = [ When(polis_journal__organization_id=org_id, then=Value(org_name)) for org_id, org_name in polis_organizations.items() ] queryset = Model1.objects.select_related('polis_journal').annotate( organization_name=Case( *when_clauses, default=Value(None), output_field=CharField() ) )
这样查询出来的每个Model1对象都会带有organization_name字段,直接访问即可。
方法三:关联Organization模型(最优解,如果有对应模型)
如果polis_organizations的数据来自另一个Django模型(比如Organization),建议给Model2添加外键关联:
# 假设存在Organization模型 class Organization(models.Model): id = models.PositiveIntegerField(primary_key=True) name = models.CharField(max_length=200) # 修改Model2 class Model2(models.Model): # 其他字段不变 organization = models.ForeignKey(Organization, on_delete=models.DO_NOTHING, db_column='organization_id') # 可以保留get_organization_name方法,或者直接通过organization.name访问 def get_organization_name(self): return self.organization.name if self.organization else None
之后就可以通过双下划线关联查询annotate:
queryset = Model1.objects.select_related('polis_journal__organization').annotate( organization_name=F('polis_journal__organization__name') )
内容的提问来源于stack exchange,提问作者Umidjon Abdugaffarov
相关产品推荐
相关产品推荐

