Как я могу контролировать порядок столбцов с помощью 'annotate', 'values' и 'union' в Django?
У меня есть 2 модели:
class Hero(models.Model):
name = models.CharField(max_length=50)
age = models.PositiveSmallIntegerField()
identity = models.TextField(max_length=50)
class Villain(models.Model):
villain_name = models.CharField(max_length=50)
age = models.PositiveSmallIntegerField()
identity = models.TextField(max_length=50)
и я создаю несколько тестовых экземпляров:
Hero(name="Superman", age=30, identity="Clark Kent").save()
Hero(name="Iron Man", age=35, identity="Tony Stark").save()
Hero(name="Spider-Man", age=18, identity="Peter Parker").save()
Villain(villain_name="Green Goblin", age=45, identity="Norman Osborn").save()
Villain(villain_name="Red Skull", age=38, identity="Johann Schmidt").save()
Villain(villain_name="Vulture", age=47, identity="Adrian Toomes").save()
Поскольку модель Villain не имеет поля name
, мы используем аннотацию перед выполнением объединения.
Затем, использование их в объединении дает результаты, где столбцы не все там, где они должны быть:
>>> from django.db.models import F
>>> characters = Hero.objects.all().values('name', 'age', 'identity').union(Villain.objects.all().annotate(name=F("villain_name")).values('name', 'age', 'identity'))
>>> for character in characters:
... print(character)
{'name': 38, 'age': 'Johann Schmidt', 'identity': 'Red Skull'}
{'name': 45, 'age': 'Norman Osborn', 'identity': 'Green Goblin'}
{'name': 47, 'age': 'Adrian Toomes', 'identity': 'Vulture'}
{'name': 'Iron Man', 'age': 35, 'identity': 'Tony Stark'}
{'name': 'Spider-Man', 'age': 18, 'identity': 'Peter Parker'}
{'name': 'Superman', 'age': 30, 'identity': 'Clark Kent'}
Просмотрев необработанные sql-запросы, мы видим следующее:
>>> str(Hero.objects.all().values('name', 'age', 'identity').query)
'SELECT "myapp_hero"."name", "myapp_hero"."age", "myapp_hero"."identity" FROM "myapp_hero"'
>>> str(Villain.objects.all().annotate(name=F("villain_name")).values('name', 'age', 'identity').query)
'SELECT "myapp_villain"."age", "myapp_villain"."identity", "myapp_villain"."villain_name" AS "name" FROM "myapp_villain"'
Автогенерируемый sql содержит столбцы, которые не расположены в одинаковом порядке для двух запросов.
Как я могу сделать их в том же порядке, чтобы не испортить результирующий кверисет моего союза?
PS. Да, это очень похоже на вопрос заданный здесь, но после долгого объяснения, почему так происходит, конечным результатом ответа на тот вопрос было:
пожалуйста, убедитесь, что порядок полей в сгенерированном SQL всегда корректен
что помогает, но не отвечает на вопрос, как это исправить.
Из docs на union
Передача различных моделей работает до тех пор, пока список SELECT одинаков во всех наборах запросов (по крайней мере, типы, имена не имеют значения, пока типы расположены в том же порядке)
Когда вы добавляете аннотацию к values()
, она всегда идет после неаннотированных столбцов, однако, поскольку имена не имеют значения и вам нужно, чтобы столбцы располагались в том же порядке, вы можете отбросить эту аннотацию
Hero.objects.values(
'name', 'age', 'identity'
).union(Villain.objects.values(
'villain_name', 'age', 'identity'
))