如何在 Django 中使用"注释"、'values'和'union'来控制列顺序?



我有两种型号:

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中的字段顺序始终正确

这有帮助,但不能回答如何修复它的问题。

来自union上的文档

只要SELECT列表在所有QuerySet中都相同,就可以传递不同的模型(至少是类型,只要类型的顺序相同,名称就无关紧要(

当您将注释添加到values()时,它总是在未注释的列之后,但是由于名称无关紧要,并且您只需要列的顺序相同,因此可以放弃此注释

Hero.objects.values(
'name', 'age', 'identity'
).union(Villain.objects.values(
'villain_name', 'age', 'identity'
))