Укажите порядок столбцов в SELECT с помощью UNION, используя Django ORM - PullRequest
6 голосов
/ 20 января 2020

Как я могу указать порядок столбцов в запросе SELECT в Django ORM?

Я пытаюсь объединить элементы из двух таблиц, но, очевидно, элементы в объединении соответствуют порядку столбцов в SELECT, а не именам столбцов (даже если имена столбцов совпадают).

Рассмотрим следующие модели:

class Person(models.Model):
    first_name = models.CharField(max_length=256)
    last_name = models.CharField(max_length=256)
    age = models.IntegerField()


class Car(models.Model):
    number = models.IntegerField()
    brand = models.CharField(max_length=256)
    name = models.CharField(max_length=256)

и следующий фрагмент кода:

Person.objects.create(first_name="John", last_name="Smith", age=25)
Car.objects.create(number=42, name="Cybertruck", brand="Tesla")

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

data = q1.union(q2)
print(data.query)
assert list(data) == [
    {'name': 'John', 'group': 'Smith', 'number': 25},
    {'name': 'Cybertruck', 'group': 'Tesla', 'number': 42},
])

Как видите, я поставил правильный порядок в .values().

Чего можно ожидать, так это того, что столбцы в объединении будут сопоставляться в порядке, передаваемом значениям (или по именам столбцов), но это происходит так:

SELECT "testunion_person"."first_name" AS "name", "testunion_person"."last_name" AS "group", "testunion_person"."age" AS "number" FROM "testunion_person" UNION SELECT "testunion_car"."name", "testunion_car"."number", "testunion_car"."brand" AS "group" FROM "testunion_car"

В запросах "testunion_car"."number" is до "testunion_car"."brand", что делает Car в UNION следующими значениями: {'name': 'Cybertruck', 'group': '42', 'number': 'Tesla'}

EDIT: я использую версию 2.2 (LTS) Django

Ответы [ 3 ]

1 голос
/ 03 февраля 2020

Не ошибка Django. Хотя столбцы запроса не сортируются как значения, набор запросов отображает правильный порядок:

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

Это происходит потому, что данные будут отсортированы после выборки из базы данных. Фрагмент исходного кода 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)
1 голос
/ 03 февраля 2020

Вместо указания псевдонима в annotate(), вы также можете указать их прямо в 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'))

Я заметил, что даже тогда не было правильного упорядочивания полей. Я переименовал поле number в xnumber, чтобы избежать конфликтов с одноименным полем модели, и все сгруппировано правильно.

0 голосов
/ 20 января 2020

Вы можете установить порядок полей, используя .values_list.

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

Проверьте документы для более подробного объяснения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...