Django ORM 中的高级过滤。需要一个查询解决方案而不是递归
Advanced filtering in Django ORM. Need a one query solution instead of recursion
我对 Django ORM 中的高级过滤有疑问。
型号:
class ClubAgentMember(models.Model):
club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name='club_agent_players')
agent = models.ForeignKey(User, on_delete=models.CASCADE, related_name='agent_club_players')
member = models.ForeignKey(User, on_delete=models.CASCADE, related_name='member_club_agents')
created_at = models.DateTimeField(auto_now_add=True)
目标是这样的:
例如,我有初始 agent_id
= 15,我需要找到连接到初始代理的代理的所有代理 ID。我知道如何通过递归来做到这一点。在小样本上很好,但在更大的样本上它会大大减慢 DB。所以我需要在 1 个查询中提取所有数据。
结果查询集应该是 [ 15, 19, 22]
– agent_id
如何阅读图表:
初始代理有 id= 15
(黄色)。 id [18, 19, 27, 28]
的成员附加到此代理(橙色)。其中一名成员(此示例中只有一名,可能是 2 名或更多名或全部,取决于 )本身就是代理人,编号 19
(绿色)。
在下一级,我们有初始代理 19
(绿色),他有成员 [22, 31, 32]
附属于他。其中一个是特工本人 22(红色)。
下一级特工ID=22
,他的成员是[37, 38, 39]
。 None 其中有代理人。所以我们到这里就完成了。
最后,我需要获得此序列中所有已连接代理(已连接到初始代理)的 ID,即我们场景中的 agent_id
[15, 19, 22]
。
初始代理 ID 来自 request.user.id
,可能会有所不同
Raw SQL 也是可能的,如果不能通过 Django ORM 做的话
Django版本我有2.0.7
提前致谢。
一种选择是将所有数据查询到内存中并在 Python 中进行递归。为了减少处理时间,可以使用 .values_list()
将查询减少到所需的最少数据(没有 ORM 转换为 类,只有原始值)并且使用 Python sets
可以允许快速查找。
代码可能与此类似:
import collections
agents = collections.defaultdict(set)
for agent_id, member_id in ClubAgentMember.objects.values_list('agent_id', 'member_id'):
agents[agent_id].add(member_id)
start_agent_id = 15
agents_to_check = {start_agent_id, }
result_agent_set = set()
while len(agents_to_check) > 0:
agent_id = agents_to_check.pop()
result_agent_set.add(agent_id)
for member_id in agents.get(agent_id, []):
if member_id in agents:
agents_to_check.add(member_id)
print('result_agent_set', result_agent_set)
你在评论中说
On a sample with 30.000 enties for example, it would be over one second (overal API time), which is not good.
对于您的用例和您的数据,此代码提案是否仍然变慢?
create materialized view players_view as
(
WITH RECURSIVE players AS (
SELECT agent_id,
player_id
FROM clubs_clubagentplayer
WHERE agent_id = 15
UNION
SELECT sub.agent_id,
sub.player_id
FROM clubs_clubagentplayer as sub
INNER JOIN players as main ON main.player_id = sub.agent_id
)
SELECT DISTINCT agent_id
FROM players
)
我对 Django ORM 中的高级过滤有疑问。
型号:
class ClubAgentMember(models.Model):
club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name='club_agent_players')
agent = models.ForeignKey(User, on_delete=models.CASCADE, related_name='agent_club_players')
member = models.ForeignKey(User, on_delete=models.CASCADE, related_name='member_club_agents')
created_at = models.DateTimeField(auto_now_add=True)
目标是这样的:
例如,我有初始 agent_id
= 15,我需要找到连接到初始代理的代理的所有代理 ID。我知道如何通过递归来做到这一点。在小样本上很好,但在更大的样本上它会大大减慢 DB。所以我需要在 1 个查询中提取所有数据。
结果查询集应该是 [ 15, 19, 22]
– agent_id
如何阅读图表:
初始代理有 id= 15
(黄色)。 id [18, 19, 27, 28]
的成员附加到此代理(橙色)。其中一名成员(此示例中只有一名,可能是 2 名或更多名或全部,取决于 )本身就是代理人,编号 19
(绿色)。
在下一级,我们有初始代理 19
(绿色),他有成员 [22, 31, 32]
附属于他。其中一个是特工本人 22(红色)。
下一级特工ID=22
,他的成员是[37, 38, 39]
。 None 其中有代理人。所以我们到这里就完成了。
最后,我需要获得此序列中所有已连接代理(已连接到初始代理)的 ID,即我们场景中的 agent_id
[15, 19, 22]
。
初始代理 ID 来自 request.user.id
,可能会有所不同
Raw SQL 也是可能的,如果不能通过 Django ORM 做的话 Django版本我有2.0.7
提前致谢。
一种选择是将所有数据查询到内存中并在 Python 中进行递归。为了减少处理时间,可以使用 .values_list()
将查询减少到所需的最少数据(没有 ORM 转换为 类,只有原始值)并且使用 Python sets
可以允许快速查找。
代码可能与此类似:
import collections
agents = collections.defaultdict(set)
for agent_id, member_id in ClubAgentMember.objects.values_list('agent_id', 'member_id'):
agents[agent_id].add(member_id)
start_agent_id = 15
agents_to_check = {start_agent_id, }
result_agent_set = set()
while len(agents_to_check) > 0:
agent_id = agents_to_check.pop()
result_agent_set.add(agent_id)
for member_id in agents.get(agent_id, []):
if member_id in agents:
agents_to_check.add(member_id)
print('result_agent_set', result_agent_set)
你在评论中说
On a sample with 30.000 enties for example, it would be over one second (overal API time), which is not good.
对于您的用例和您的数据,此代码提案是否仍然变慢?
create materialized view players_view as
(
WITH RECURSIVE players AS (
SELECT agent_id,
player_id
FROM clubs_clubagentplayer
WHERE agent_id = 15
UNION
SELECT sub.agent_id,
sub.player_id
FROM clubs_clubagentplayer as sub
INNER JOIN players as main ON main.player_id = sub.agent_id
)
SELECT DISTINCT agent_id
FROM players
)