Django ORM 在一个查询中加入多对多关系
Django ORM join many to many relation in one query
如果我们有 2 个模型 A、B 具有多对多关系。
我想获得类似于这样的 sql 查询:
SELECT *
FROM a LEFT JOIN ab_relation
ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
所以在 django 中,当我尝试时:
A.objects.prefetch_related('bees')
我收到 2 个类似的查询:
SELECT * FROM a;
SELECT ab_relation.a_id AS prefetch_related_val_a_id, b.*
FROM b JOIN ab_relation ON b.id = ab_relation.b_id
WHERE ab_relation.a_id IN (123, 456... list of all a.id);
考虑到 A 和 B 有中等大的表,我发现 django 的方式对我的需要来说太慢了。
问题是:是否可以通过ORM获取left join手写查询?
编辑以回答一些说明:
是的,LEFT OUTER JOIN
更适合获取查询集中的所有 A,而不仅仅是与 B 有关系的那些(已更新 sql)。
中等大意味着每行约 4k 行,太慢意味着约 3 秒(第一次加载时,在 Redis 缓存之前。)请记住页面上还有其他查询。
实际上是的,我们只需要 B.one_field 但是尝试使用 Prefetch('bees', queryset=B.objects.values('one_field'))
一个错误说你不能在预取中使用 values
。
查询集将用作多 select 表单字段的选项,我们需要用来自B.field.
直接回答请跳至第 6 点)
我们一步步说吧
1) N:M select。你说你想要这样的查询:
SELECT *
FROM a JOIN ab_relation ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
但这不是真正的 N:M 查询,因为您只获取 A-B 相关对象查询应该使用 outer joins
。至少喜欢:
SELECT *
FROM a left outer JOIN
ab_relation ON ab_relation.a_id = a.id left outer JOIN
b ON ab_relation.b_id = b.id;
在其他情况下,您只会获得 A
个具有相关 B
的模型。
2)读大tables你说"moderately big tables"。那么,您确定要从数据库中读取整个 table 吗?这在读取大量数据的 Web 环境中并不常见,在这种情况下,您可以对数据进行分页。可能不是网络应用程序?为什么你需要阅读这么大的 tables?我们需要上下文来回答您的问题。您确定需要两个 table 的所有字段吗?
3) Select * 来自 您确定需要来自两个 table 的所有字段吗?如果您只读取某些值,则此查询可能 运行 更快。
A.objects.values( "some_a_field", "anoter_a_field", "Bs__some_b_field" )
4) 总结。 ORM是一个强大的工具,两个单读操作是"fast"。我写了一些想法,但也许我们需要更多的背景来回答你的问题。什么意思是中等大 tables,小麦意味着慢,你用这些数据做什么,每个 table 的每行有多少字段或字节,......
Editedd 因为 OP 已经编辑了问题。
5) 使用正确的 UI 控件。你说:
The queryset will be used as options for a multi-select form-field, where we will need to represent A objects that have a relation with B with an extra string from the B.field.
向客户端发送表单的 4k 行似乎是一种反模式。我建议您转移到只加载所需数据的实时控件。例如,按某些文本进行过滤。看看 django-select2 很棒的项目。
6) 你说
The question is: Is it possible to obtain the left join manually written query through the ORM?
答案是:是,你可以使用values
来完成,正如我在第3点所说的。示例:Material
和ResultatAprenentatge
是一个 N:M 关系:
>>> print( Material
.objects
.values( "titol", "resultats_aprenentatge__codi" )
.query )
查询:
SELECT "material_material"."titol",
"ufs_resultataprenentatge"."codi"
FROM "material_material"
LEFT OUTER JOIN "material_material_resultats_aprenentatge"
ON ( "material_material"."id" =
"material_material_resultats_aprenentatge"."material_id" )
LEFT OUTER JOIN "ufs_resultataprenentatge"
ON (
"material_material_resultats_aprenentatge"."resultataprenentatge_id" =
"ufs_resultataprenentatge"."id" )
ORDER BY "material_material"."data_edicio" DESC
如果我们有 2 个模型 A、B 具有多对多关系。
我想获得类似于这样的 sql 查询:
SELECT *
FROM a LEFT JOIN ab_relation
ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
所以在 django 中,当我尝试时:
A.objects.prefetch_related('bees')
我收到 2 个类似的查询:
SELECT * FROM a;
SELECT ab_relation.a_id AS prefetch_related_val_a_id, b.*
FROM b JOIN ab_relation ON b.id = ab_relation.b_id
WHERE ab_relation.a_id IN (123, 456... list of all a.id);
考虑到 A 和 B 有中等大的表,我发现 django 的方式对我的需要来说太慢了。
问题是:是否可以通过ORM获取left join手写查询?
编辑以回答一些说明:
是的,
LEFT OUTER JOIN
更适合获取查询集中的所有 A,而不仅仅是与 B 有关系的那些(已更新 sql)。中等大意味着每行约 4k 行,太慢意味着约 3 秒(第一次加载时,在 Redis 缓存之前。)请记住页面上还有其他查询。
实际上是的,我们只需要 B.one_field 但是尝试使用
Prefetch('bees', queryset=B.objects.values('one_field'))
一个错误说你不能在预取中使用values
。查询集将用作多 select 表单字段的选项,我们需要用来自B.field.
直接回答请跳至第 6 点)
我们一步步说吧
1) N:M select。你说你想要这样的查询:
SELECT *
FROM a JOIN ab_relation ON ab_relation.a_id = a.id
JOIN b ON ab_relation.b_id = b.id;
但这不是真正的 N:M 查询,因为您只获取 A-B 相关对象查询应该使用 outer joins
。至少喜欢:
SELECT *
FROM a left outer JOIN
ab_relation ON ab_relation.a_id = a.id left outer JOIN
b ON ab_relation.b_id = b.id;
在其他情况下,您只会获得 A
个具有相关 B
的模型。
2)读大tables你说"moderately big tables"。那么,您确定要从数据库中读取整个 table 吗?这在读取大量数据的 Web 环境中并不常见,在这种情况下,您可以对数据进行分页。可能不是网络应用程序?为什么你需要阅读这么大的 tables?我们需要上下文来回答您的问题。您确定需要两个 table 的所有字段吗?
3) Select * 来自 您确定需要来自两个 table 的所有字段吗?如果您只读取某些值,则此查询可能 运行 更快。
A.objects.values( "some_a_field", "anoter_a_field", "Bs__some_b_field" )
4) 总结。 ORM是一个强大的工具,两个单读操作是"fast"。我写了一些想法,但也许我们需要更多的背景来回答你的问题。什么意思是中等大 tables,小麦意味着慢,你用这些数据做什么,每个 table 的每行有多少字段或字节,......
Editedd 因为 OP 已经编辑了问题。
5) 使用正确的 UI 控件。你说:
The queryset will be used as options for a multi-select form-field, where we will need to represent A objects that have a relation with B with an extra string from the B.field.
向客户端发送表单的 4k 行似乎是一种反模式。我建议您转移到只加载所需数据的实时控件。例如,按某些文本进行过滤。看看 django-select2 很棒的项目。
6) 你说
The question is: Is it possible to obtain the left join manually written query through the ORM?
答案是:是,你可以使用values
来完成,正如我在第3点所说的。示例:Material
和ResultatAprenentatge
是一个 N:M 关系:
>>> print( Material
.objects
.values( "titol", "resultats_aprenentatge__codi" )
.query )
查询:
SELECT "material_material"."titol",
"ufs_resultataprenentatge"."codi"
FROM "material_material"
LEFT OUTER JOIN "material_material_resultats_aprenentatge"
ON ( "material_material"."id" =
"material_material_resultats_aprenentatge"."material_id" )
LEFT OUTER JOIN "ufs_resultataprenentatge"
ON (
"material_material_resultats_aprenentatge"."resultataprenentatge_id" =
"ufs_resultataprenentatge"."id" )
ORDER BY "material_material"."data_edicio" DESC