Specify order of columns in SELECT with UNION using Django ORM Specify order of columns in SELECT with UNION using Django ORM postgresql postgresql

Specify order of columns in SELECT with UNION using Django ORM


Instead of specifying the alias under annotate(), you can also specify them straight under values():

q1 = Person.objects.all().values(    name=F('first_name'), group=F('last_name'), xnumber=F('age'))q2 = Car.objects.all().values(    'name', group=F('brand'), xnumber=F('number'))

I noticed that even then, it wasn't ordering the fields properly. I renamed the number field to xnumber to avoid conflicts with the model field of the same name and everything is grouped properly.


You can set the order of the fields using .values_list.

qs1 = Person.objects.values_list('name', 'group', 'number')qs2 = Car.objects.values_list('brand', 'name', 'number')qs1.union(qs2)

Check the docs for more detailed explanation.


Not a Django bug. Although query columns not sorted as values, the queryset display the right order:

In [13]: print(data)<QuerySet [{'name': 'Cybertruck', 'group': 42, 'number': 'Tesla'}, {'name': 'John', 'group': 'Smith', 'number': 25}]>

It is because the data will be sorted after fetch from database. Source code snippet of QuerySet:

class QuerySet:    def __iter__(self):        """        The queryset iterator protocol uses three nested iterators in the        default case:            1. sql.compiler.execute_sql()               - Returns 100 rows at time (constants.GET_ITERATOR_CHUNK_SIZE)                 using cursor.fetchmany(). This part is responsible for                 doing some column masking, and returning the rows in chunks.            2. sql.compiler.results_iter()               - Returns one row at time. At this point the rows are still just                 tuples. In some cases the return values are converted to                 Python values at this location.            3. self.iterator()               - Responsible for turning the rows into model objects.        """        self._fetch_all()        return iter(self._result_cache)