Lin*_*lle 5 django django-orm django-queryset
我有三个相关的模型:Process,Factor和Level. AProcess与Factors 存在多对多关系,aFactor将拥有一个或多个Levels。我正在尝试计算Level与 a 相关的s 的所有组合Process。这很容易用 Pythonitertools作为模型方法来实现,但执行速度有点慢,所以我试图弄清楚如何使用 Django ORM 在 SQL 中执行这个计算。
楷模:
class Process(models.Model):
factors = models.ManyToManyField(Factor, blank = True)
class Factor(models.Model):
...
class Level(models.Model):
factor = models.ForeignKey(Factor, on_delete=models.CASCADE)
Run Code Online (Sandbox Code Playgroud)
示例:一个过程'Running'涉及三个Factors ( 'Distance', 'Climb', 'Surface'),每个s由多个Levels ( 'Long'/ 'Short', 'Flat'/ 'Hilly', 'Road'/ 'Mixed'/ 'Trail') 组成。计算 SQL 中的组合将涉及通过首先确定Factor涉及多少s(在此示例中为 3)并CROSS JOIN多次执行所有级别的a来构建查询。
在 SQL 中,这可以这样完成:
WITH foo AS
(SELECT * FROM Level
WHERE Level.factor_id IN
(SELECT ProcessFactors.factor_id FROM ProcessFactors WHERE process_id = 1)
)
SELECT a1.*, a2.*, a3.*
FROM foo a1
CROSS JOIN foo a2
CROSS JOIN foo a3
WHERE (a1.factor_id < a2.factor_id) AND (a2.factor_id < a3.factor_id)
Run Code Online (Sandbox Code Playgroud)
结果:
a1.name | a2.name | a3.name
--------------------------
Long | Flat | Road
Long | Flat | Mixed
Long | Flat | Trail
Long | Hilly | Road
Long | Hilly | Mixed
Long | Hilly | Trail
Short | Flat | Road
Short | Flat | Mixed
Short | Flat | Trail
Short | Hilly | Road
Short | Hilly | Mixed
Short | Hilly | Trail
Run Code Online (Sandbox Code Playgroud)
目前,我将此作为Process模型上的方法实现为:
a1.name | a2.name | a3.name
--------------------------
Long | Flat | Road
Long | Flat | Mixed
Long | Flat | Trail
Long | Hilly | Road
Long | Hilly | Mixed
Long | Hilly | Trail
Short | Flat | Road
Short | Flat | Mixed
Short | Flat | Trail
Short | Hilly | Road
Short | Hilly | Mixed
Short | Hilly | Trail
Run Code Online (Sandbox Code Playgroud)
这是否可以使用 Django ORM 或者它是否足够复杂以至于它应该作为原始查询实现以提高 Python 代码实现的速度?
几年前有一个关于在 Django ORM 中执行CROSS JOIN的类似问题(看起来像 Django v1.3)似乎并没有引起太多关注(作者试图只使用 Python itertools)。
from itertools import groupby, product
def level_combinations(self):
# We need order by factor_id for proper grouping
levels = Level.objects.filter(factor__process=self).order_by('factor_id')
# [{'name': 'Long', 'factor_id': 1, ...},
# {'name': 'Short', 'factor_id': 1, ...},
# {'name': 'Flat', 'factor_id': 2, ...},
# {'name': 'Hilly', 'factor_id': 2, ...}]
groups = [list(group) for _, group in groupby(levels, lambda l: l.factor_id)]
# [[{'name': 'Long', 'factor_id': 1, ...},
# {'name': 'Short', 'factor_id': 1, ...}],
# [{'name': 'Flat', 'factor_id': 2, ...},
# {'name': 'Hilly', 'factor_id': 2, ...}]]
# Note: don't forget, that product is iterator/generator, not list
return product(*groups)
Run Code Online (Sandbox Code Playgroud)
如果顺序不重要,那么:
def level_combinations(self):
levels = Level.objects.filter(factor__process=self)
groups = {}
for level in levels:
groups.setdefault(level.factor_id, []).append(level)
return product(*groups.values())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1596 次 |
| 最近记录: |