Преобразовать значение поля в django RawQueryset в другой тип поля django - PullRequest
0 голосов
/ 01 мая 2018

У меня довольно сложный запрос, который генерирует Django RawQuerySet. Этот конкретный запрос возвращает некоторые поля, которые не являются частью модели, на которой основан RawQuerySet, поэтому я использую .annotate(field_name=models.Value('field_name')), чтобы прикрепить его в качестве атрибута к отдельным записям в RawQuerySet. Наиболее важным настраиваемым полем на самом деле является uuid, которое я использую для составления URL-адресов с использованием функциональности Django {% url %}.

Вот проблема: я не использую стандартные uuid s внутри своего приложения, я использую SmallUUIDs (сжатые UUID.) Они сохраняются в базе данных как собственные uuidfield s преобразован в более короткие строки в Python. Поэтому мне нужно каким-то образом преобразовать uuid, возвращенный как часть RawQuerySet, в SmallUUID для использования внутри шаблона для генерации URL.

Мой код выглядит примерно так:

query = "SELECT othertable.uuid_field as my_uuid FROM myapp_mymodel
         JOIN othertable o ON myapp_mymodel.x = othertable.x"

MyModel.objects.annotate(
    my_uuid=models.Value('my_uuid'),
).raw(query)

Теперь здесь есть логическое решение, есть дополнительный kwarg для models.Value, называемый output_field, благодаря которому код выглядит следующим образом:

MyModel.objects.annotate(
    my_uuid=models.Value('my_uuid', output_field=SmallUUIDField()),
).raw(query)

Но это не работает! Этот kwarg полностью игнорируется, и тип атрибута основан на типе, возвращаемом из базы данных, а не на том, что в output_field. В моем случае я получаю вывод uuid, потому что Postgres возвращает тип UUID, но если бы я изменил запрос на SELECT cast othertable.uuid_field as text) as my_uuid, я бы получил атрибут в формате строки. Похоже, что Django (по крайней мере, версия 1.11.12) на самом деле не волнует, что находится в этом kwarg в этом случае.

Итак, вот что я думаю, это мои потенциальные решения в произвольном порядке:

  1. Изменить способ форматирования запроса (в Django или в SQL)
  2. Как-то изменить результирующий набор RawQuerySet, прежде чем он будет передан в представление
  3. Измените что-то внутри шаблонов, чтобы преобразовать UUID в smalluuid для использования в обратном процессе URL.

Каковы мои лучшие следующие шаги здесь?

1 Ответ

0 голосов
/ 04 мая 2018

Несколько проблем с вашим текущим подходом:

  1. Value() не делает то, о чем вы думаете - ваша аннотация буквально просто аннотирует каждую строку значением «my_uuid», потому что это то, что вы ей передали. Он не ищет поле с таким именем (для этого вам нужно использовать F выражений).

  2. Точка 1 выше не имеет значения, потому что, как только вы используете raw(), аннотация игнорируется - вот почему вы не видите никакого эффекта от нее.

Суть в том, что попытка аннотировать RawQuerySet не будет легкой. Есть аргумент translations, который он принимает, но я не могу придумать, как заставить его работать с типом объединения, которое вы используете.

Следующее лучшее предложение, которое я могу придумать, это то, что вы просто вручную конвертируете поле в SmallUUID объект, когда вам это нужно - что-то вроде этого:

from smalluuid import SmallUUID

objects = MyModel.objects.raw(query)

for o in objects:
    # Take the hex string obtained from the database and convert it to a SmallUUID object.
    # If your database has a built-in UUID type you will need to do 
    # SmallUUID(small=o.my_uuid) instead.
    my_uuid = SmallUUID(hex=o.my_uuid)

(я делаю это в цикле только для иллюстрации - в зависимости от того, где вам это нужно, вы можете сделать это в теге шаблона или в представлении).

...