Как получить псевдоним таблицы для внутреннего соединения в django

#python #django #django-filter #django-annotate #django-subquery

#python #django #django-filter #django-аннотировать #django-подзапрос

Вопрос:

Я использую django 2.1, python 3.6 и SQL Server 2012 в качестве серверной части. У меня есть следующие модели:

 class ModelA(models.Model):
    name = models.CharField(...)
    value = models.PositiveIntegerField(...)

class ModelB(models.Model):
    name = models.CharField(...)
    values = models.ManyToManyField(ModelA, through='ModelC')

class ModelC(models.Model):
    model_a = models.ForeignKey(ModelA, ...)
    model_b = models.ForeignKey(ModelB, ...)
    info_a = models.CharField(...)
    info_b = models.CharField(...)
  

Как я могу выполнить следующий SQL-запрос:

 SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in (1,2) AND t2.model_a_id in (8,9,10,11) AND t4.model_a_id in (21,22)
  

Что у меня есть на данный момент:

 ModelB.objects.filter(values__in=[1,2]).filter(values__in=[8,9,10,11]).filter(values__in=[21,22])
  

Который создает правильный отфильтрованный набор запросов. Но как я могу получить правильные поля?

Я пытался использовать annotate функцию, но у меня не получилось. Использование django’s Subquery , как описано в документах, создает ошибку базы данных, поскольку SQL Server не поддерживает подзапросы в SELECT части.

Какие-либо рекомендации? Спасибо!

Ответ №1:

Я решил это, не возвращаясь к необработанному sql. Я использовал FilteredRealtion объекты django в сочетании с дополнительными annotate объектами, подобными этому:

 from django.db.models import Q, F, FilteredRelation

qs = ModelB.objects.filter(values__in=[21,22])
qs = qs.filter(values__in=[1,2])
qs = qs.filter(values__in=[8,9,10,11])
qs = qs.annotate(_a_id=FilteredRelation('modelc', condition=Q(values__in=[8,9,10,11])),
                 _a_value=FilteredRelation('modelc', condition=Q(values__in=[1,2])))
qs = qs.annotate(a_id=F('_a_id__model_a'), a_value=F('_a_value__model_a__value'))
qs = qs.values('a_id', 'a_value')
  

Ответ №2:

Ваш запрос не является оптимальным, но это другая проблема. Вы можете попробовать необработанный запрос

Чтобы вы могли запустить:

 query = """
SELECT t1.model_a_id AS a_id, t3.value AS a_value
FROM ModelB AS t0
INNER JOIN ModelC t1 ON t1.model_b_id = t0.id
INNER JOIN ModelC t2 ON t2.model_b_id = t0.id
INNER JOIN ModelA t3 ON t3.id = t2.model_a_id
INNER JOIN ModelC t4 ON t4.model_b_id = t0.id
WHERE t1.model_a_id in ({0}) AND t2.model_a_id in ({1}) AND t4.model_a_id in 
({2})"""
t1_filters = ','.join(['1','2'])
t2_filters = ','.join(['8', '9', '10', '11'])
t4_filters = ','.join(['21', '22'])

results = ModelA.objects.raw(query.format(t1_filters, t2_filters, t4_filters))
for instance in results.all():
    print(instance)