Django ORM在一个查询中加入多对多关系

时间:2018-11-20 08:15:31

标签: django postgresql django-models django-orm

如果我们有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 OUTER JOIN来获得查询集中的所有A,而不仅仅是与B有关系的SQL(更新的sql)。

  • 适度的大意味着每个行大约4k行,太慢的意味着大约3秒(在第一次加载时,在redis缓存之前。)请记住,页面上还有其他查询。

  • 实际上是的,我们只需要B.one_field,但是尝试使用Prefetch('bees', queryset=B.objects.values('one_field'))时出现错误,提示您无法在预取中使用values

  • 查询集将用作多选表单域的选项,在这里我们需要用B.field中的额外字符串表示与B有关系的A对象。

1 个答案:

答案 0 :(得分:2)

有关直接答案,请跳至第6点)

让我们一步一步地讲。

1) N:M选择。您说您想要这样的查询:

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的{​​{1}}个模型。

2)阅读大表,您说的是“中度大表” 。然后,确定要从数据库读取整个表吗?在Web环境中,读取大量数据并不常见,在这种情况下,您可以分页数据。可能不是网络应用?为什么需要阅读这张大桌子?我们需要上下文来回答您的问题。您确定需要两个表中的所有字段吗?

3)选择* from 您确定您需要两个表中的所有字段吗?可能是,如果您仅读取某些值,则此查询将运行得更快。

B

4)摘要。 ORM是一个功能强大的工具,两次读取操作都是“快速”的。我写了一些想法,但也许我们需要更多的上下文来回答您的问题。什么意味着中等大小的表,小麦意味着缓慢,您在处理这些数据,每个表的每一行有多少个字段或字节,...。

已编辑,因为OP已编辑了问题。

5)使用正确的UI控件。你说:

  

queryset将用作多选表单域的选项,在这里我们需要用B.field中的额外字符串表示与B有关系的A对象。

向表单发送4k行到客户端看起来像是反模式。我建议您转到仅加载所需数据的实时控件。例如,按某些文本进行过滤。看看django-select2很棒的项目。

6)你说

  

问题是:是否可以通过ORM获得左联接手动编写的查询?

答案是:,您可以使用A.objects.values( "some_a_field", "anoter_a_field", "Bs__some_b_field" ) 进行操作,正如我在第3点所说。示例:valuesMaterial是一个N:M关系:

ResultatAprenentatge

查询:

>>> print( Material
          .objects
          .values( "titol", "resultats_aprenentatge__codi" )
          .query )