使用Django ORM计算组合(CROSS JOIN)

林维尔

我有三个相关的模型:过程,因子和水平。流程与因素具有多对多关系,而因素将具有一个或多个级别。我正在尝试计算与流程有关的所有关卡组合。使用Python的itertools作为模型方法可以直接实现,但是执行速度有些慢,因此我试图弄清楚如何使用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)

示例:流程运行涉及三个因子(距离爬升表面),每个因子由多个级别组成(长/短平坦/陡峭道路/混合/足迹)。计算SQL中的组合将涉及通过首先确定涉及多少因素(在此示例中为3)并执行CROSS JOIN所有级别的多次来构建查询

在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)

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

目前,我已经将此作为过程模型的一种方法实现为:

def level_combinations(self):
    levels = []
    for factor in self.factors.all():
        levels.append(Level.objects.filter(factor = factor))

    combinations = []
    for levels in itertools.product(*levels):
        combination = {}

        combination["levels"] = levels

        combinations.append(combination)

    return combinations

是否可以使用Django ORM做到这一点,或者它是否足够复杂以至于应将其作为原始查询来实现,以提高Python代码实现的速度?

几年前,有一个关于在Django ORM中执行性能CROSS JOIN的类似问题(大约是Django v1.3)似乎没有引起人们的广泛关注(作者只喜欢使用Python itertools)。

vsd
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)

如果顺序无关紧要,则:

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())

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Join with subquery in Django ORM

来自分类Dev

Django ORM JOIN查询

来自分类Dev

使用CROSS JOIN

来自分类Dev

使用CROSS JOIN

来自分类Dev

如何使用Django Join

来自分类Dev

如何使用 Django 的 ORM (1.11) 从 LEFT JOIN 的表/模型中选择一个字段

来自分类Dev

使用名称不同的字段创建Django Join

来自分类Dev

如何强制 Django 使用“JOIN VALUES”

来自分类Dev

使用join和条件组合一列

来自分类Dev

在这里使用CROSS JOIN是个坏主意吗?

来自分类Dev

使用 MySQL,如何使用 Left Join 记录计算 MEDIAN?

来自分类Dev

如何使用Django ORM查询以计算数量乘以价格

来自分类Dev

通过计算多行结果使用JOIN更新表

来自分类Dev

将LEFT OUTER JOIN查询转换为Django ORM queryset / query

来自分类Dev

如何使用JOIN、CROSS JOIN将SQL中全球化的存储值合并成一个表

来自分类Dev

Django INNER JOIN使用带WHERE子句的外键

来自分类Dev

如何使用 Group By 和 Join 访问对象 User (django admin)?

来自分类Dev

Django +使用/计算

来自分类Dev

SqlAlchemy ORM中join的理解

来自分类Dev

SQL INNER JOIN与使用WHERE的LEFT JOIN

来自分类Dev

CROSS JOIN无法正常运作

来自分类Dev

使用Django ORM添加列

来自分类Dev

使用php写入django ORM

来自分类Dev

使用join的替代语法

来自分类Dev

如何使用join?

来自分类Dev

在JOIN上使用按键

来自分类Dev

使用ON或JOIN的SQL查询

来自分类Dev

Netezza使用JOIN删除

来自分类Dev

在何处使用Join