我正在尝试使用 Django 3.2.9 和 DRF 3.12.4 从我的 postgreSQL 数据库中选择数据。
这是我的模型:
class Headpiece(ValidatedModelbase):
class Types(models.TextChoices):
POWDER = "Powder"
SOLUTION = "Solution"
id_headpiece = models.AutoField(primary_key=True)
name = models.TextField(null=False)
structure = models.TextField(null=False)
total_structure = models.TextField(null=False)
molecular_weight = models.FloatField(null=False)
type = models.CharField(null=False, choices=Types.choices, max_length=255)
status = models.BooleanField(default=True)
comment = models.TextField(null=True, blank=True)
class Meta:
db_table = "headpiece"
managed = True
def __str__(self):
return self.name
class Matter(ValidatedModelbase):
id_matter = models.AutoField(primary_key=True)
dna_tag = models.ForeignKey(DNATag, on_delete=models.DO_NOTHING, null=True, blank=True)
headpiece = models.ForeignKey(Headpiece, on_delete=models.DO_NOTHING, null=True, blank=True, related_name='matter')
class Meta:
db_table = "matter"
managed = True
class Vial(ValidatedModelbase):
id_vial = models.AutoField(primary_key=True)
rack = models.ForeignKey(Rack, on_delete=models.DO_NOTHING)
matter = models.ForeignKey(Matter, on_delete=models.DO_NOTHING, related_name='vial')
barcode = models.CharField(null=False, max_length=255)
position_column = models.IntegerField(null=False)
position_row = models.IntegerField(null=False)
quantity_left = models.FloatField(null=False)
project = models.ForeignKey(Project, on_delete=models.DO_NOTHING, null=True, blank=True)
reserving_business_entity_name = models.CharField(null=True, max_length=255)
origin_name = models.CharField(null=True, max_length=255)
class Meta:
db_table = "vial"
unique_together = ["rack", "position_column", "position_row"]
managed = True
def __str__(self):
return self.barcode
我需要的数据是头饰列表,以及它们的小瓶数据。我想要为每个小瓶添加一条新行,如果没有相关小瓶,则仅显示头件数据。 在 SQL 中,它翻译成这样:
SELECT headpiece.*, vial.*
FROM headpiece
LEFT JOIN matter on matter.headpiece_id = headpiece.id_headpiece
LEFT JOIN vial on vial.matter_id = matter.id_matter
LIMIT 100
此查询的结果正是我需要 API 返回的结果。
这是我目前使用 DRF 进行查询的方式:
queryset = Headpiece.objects.select_related(
'matter__vial',
'matter__vial__rack',
'matter__vial__project',
).values(
'id_headpiece',
'matter__vial',
).filter(
id_headpiece=5,
)
print('count')
print(queryset.count())
print(queryset.query)
return queryset.all()
结果很好,但是如果我尝试过滤我的数据,如下所示:
queryset = Headpiece.objects.select_related(
'matter__vial',
'matter__vial__rack',
'matter__vial__project',
).values(
'id_headpiece',
'matter__vial',
).filter(
id_headpiece=5,
matter__vial__id_vial=4525
)
print('count')
print(queryset.count())
print(queryset.query)
return queryset.all()
-> ORM 添加了不需要的连接,绕过了过滤器:这是生成的 SQL :
SELECT "headpiece"."id_headpiece", "vial"."id_vial" FROM "headpiece"
LEFT OUTER JOIN "matter" ON ("headpiece"."id_headpiece" = "matter"."headpiece_id")
LEFT OUTER JOIN "vial" ON ("matter"."id_matter" = "vial"."matter_id")
INNER JOIN "matter" T4 ON ("headpiece"."id_headpiece" = T4."headpiece_id")
INNER JOIN "vial" T5 ON (T4."id_matter" = T5."matter_id")
WHERE ("headpiece"."id_headpiece" = 5 AND T5."id_vial" = 4525)
当我需要的是:
SELECT headpiece.*, vial.*
FROM headpiece
LEFT JOIN matter on matter.headpiece_id = headpiece.id_headpiece
LEFT JOIN vial on vial.matter_id = matter.id_matter
where vial.id_vial=4525
LIMIT 100
我想我因为反向关系而陷入困境,虽然我可能是错的,但我认为这是使用 SQL 组织数据的一种非常标准的方式。
我知道我可以使用原始 SQL,但我需要对查询应用大量过滤器、分页和排序,使用 ORM 会容易得多。另外,如果使用 Django ORM 无法解决此问题,我会感到惊讶。
.values(…)
[Django-doc] 和 .filter(…)
[Django-doc] 组合在一起的结果: .values(…)
将生成 LEFT OUTER JOIN
,因为项目可以是 NULL
,而 .filter(…)
看到结果不是 NULL
,因此它会将其优化为 INNER JOIN
s。
但实际上你(可能)不应该首先使用
.values(…)
:它是一种 反模式 [Django 反模式]:
queryset = Headpiece.objects.select_related(
'matter__vial',
'matter__vial__rack',
'matter__vial__project',
).filter(id_headpiece=5, matter__vial__id_vial=4525)
但是,根据查询,您也不应该预取
rack
和 project
,所以:
queryset = Headpiece.objects.select_related(
'matter__vial',
).filter(id_headpiece=5, matter__vial__id_vial=4525)