У меня есть три связанные модели: Process
, Factor
и Level
. Process
имеет отношения "многие ко многим" с Factor
s, а Factor
будет иметь один или несколько Level
s. Я пытаюсь рассчитать все комбинации Level
, связанные с Process
. Это легко реализовать с помощью 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)
Пример: Процесс 'Running'
включает в себя три Factor
s ('Distance'
, 'Climb'
, 'Surface'
), каждый из которых состоит из нескольких Level
s ('Long'
/'Short'
, 'Flat'
/'Hilly'
, 'Road'
/'Mixed'
/'Trail'
). Вычисление комбинаций в SQL будет включать построение запроса, сначала определяя, сколько Factor
было задействовано (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
В настоящее время я реализовал это как метод для модели Process
как:
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?
Несколько лет назад возник похожий вопрос о выполнении CROSS JOIN
в Django ORM (примерно Django v1.3 выглядит так) не привлекал особого внимания (автор решил просто использовать Python itertools).