Django 根据另一个模型字段注释值

Django annotate value based on another model field

我有这两个模型,Cases 和 Specialties,就像这样:

class Case(models.Model):
   ...
   judge = models.CharField()
   ....

class Specialty(models.Model):
   name = models.CharField()
   sys_num = models.IntegerField()

我知道这听起来像是一个非常奇怪的结构,但试着告诉我: Case 模型中的字段 judge 引用 Specialty 实例 sys_num 值(judge 是一个字符字段,但它总是携带一个整数)(每个 Specialty 实例具有唯一性 sys_num)。所以我可以使用类似这样的方法获取与特定 Case 实例相关的 Specialty 名称:

my_pk = #some number here...
my_case_judge = Case.objects.get(pk=my_pk).judge
my_specialty_name = Specialty.objects.get(sys_num=my_case_judge)

我知道这听起来很奇怪,但我无法更改表的基本模式,只能使用 sql 和 Django 的 orm 解决它。

我的问题是:我想在 Cases 的查询集中注释 values().

的名称

我只设法使用 CaseWhen 让它工作,但它不是动态的。如果我添加更多 Specialty 个实例,我将不得不手动更改代码。

 cases.annotate(
         specialty=Case(
             When(judge=0, then=Value('name 0 goes here')),
             When(judge=1, then=Value('name 1 goes here')),
             When(judge=2, then=Value('name 2 goes here')),
             When(judge=3, then=Value('name 3 goes here')),
             ...

这可以动态完成吗?我查看了 django's query reference 文档,但无法使用此处指定的工具生成有效的解决方案。

您可以使用 subquery expression:

from django.db.models import OuterRef, Subquery

Case.objects.annotate(
    specialty=<strong>Subquery(</strong>
        Specialty.objects.filter(<strong>sys_num=OuterRef('judge')</strong>).values('name')[:1]
    <strong>)</strong>
)

对于某些数据库,甚至可能需要转换:

from django.db.models import IntegerField, OuterRef, Subquery
from django.db.models.functions import Cast

Case.objects.annotate(
    specialty=<strong>Subquery(</strong>
        Specialty.objects.filter(sys_num=<b>Cast(</b>
            OuterRef('judge'),
            output_field=IntegerField()
        <b>)</b>).values('name')[:1]
    <strong>)</strong>
)

但是建模非常不好。通常最好使用 ForeignKey,这将保证 judge 只能指向有效的情况(因此引用完整性),将在字段上创建索引,并且还会使Django ORM 更有效,因为它允许使用(相对)小查询进行更高级的查询。