Как переименовать элементы в values ​​() в Django?

102

Я хочу сделать то же самое, что и в этом билете на djangoproject.com , но с некоторым дополнительным форматированием. Из этого запроса

>>> MyModel.objects.values('cryptic_value_name')
[{'cryptic_value_name': 1}, {'cryptic_value_name': 2}]

Я хочу получить что-то подобное:

>>> MyModel.objects.values(renamed_value='cryptic_value_name')
[{'renamed_value': 1}, {'renamed_value': 2}]

Есть ли другой, более встроенный способ, или мне придется делать это вручную?

Мартин
источник

Ответы:

72

Это немного взломано, но вы можете использовать extraметод:

MyModel.objects.extra(
  select={
    'renamed_value': 'cryptic_value_name'
  }
).values(
  'renamed_value'
)

В основном это делается SELECT cryptic_value_name AS renamed_valueв SQL.

Другой вариант, если вам всегда нужна переименованная версия, но у базы данных есть загадочное имя, - присвоить вашему полю новое имя, но использовать db_columnдля ссылки на исходное имя в базе данных.

Дэниел Розман
источник
6
По-видимому, в 1.7. * Вы сможете просто писать имена с псевдонимами прямо как именованные аргументы в values(). Источник: code.djangoproject.com/ticket/16735
gregoltsov 07
19
-1, потому что .values(supports__through_tables)и этот хак не делает (кстати, вероятно, наиболее очевидный вариант использования для переименования ValuesQuerySetключей dict)
wim
1
Кто-нибудь нашел способ сделать это через таблицы (.values ​​(supports__through_tables))?
François Constant
12
К сожалению, это решение не поддерживает связанные имена, такие как .values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha
13
Model.objects.annotate (my_alias = F ( 'some__long__name__to__alias')) значения ( 'my_alias') от билета 16735..
Юджине
189

Из django>=1.8вы можете использовать аннотацию и объект F

from django.db.models import F

MyModel.objects.annotate(renamed_value=F('cryptic_value_name')).values('renamed_value')

Также extra()будет объявлен устаревшим из документации django:

Это старый API, от которого мы намерены отказаться в какой-то момент в будущем. Используйте его только в том случае, если вы не можете выразить свой запрос с помощью других методов набора запросов. Если вам все же нужно его использовать, отправьте заявку, используя ключевое слово QuerySet.extra для вашего варианта использования (сначала проверьте список существующих заявок), чтобы мы могли улучшить API QuerySet, чтобы можно было удалить extra (). Мы больше не улучшаем и не исправляем ошибки для этого метода.

алехандроднм
источник
3
Этот ответ только работает django>=1.8. Выражения запроса позволяют annotateпринимать выражения, отличные от агрегатов. Ссылка: docs.djangoproject.com/en/1.9/releases/1.8/…
Yeo,
3
Это работает, но разве синтаксис, предложенный в исходном вопросе, не намного лучше ? MyModel.objects.values(renamed_value='cryptic_value_name')
wim
11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))работает
jarcoal
1
Этот ответ работает, но является избыточным. Ключевое слово аргументы передаются values()пропускаются через annotate()от вашего имени (см docs.djangoproject.com/en/2.2/ref/models/querysets/... ). Ответ MyModel.objects.values(renamed_value=F('cryptic_value_name'))проще и понятнее, ИМО.
tobias.mcnulty 04
77

Без использования какого-либо другого метода менеджера (проверенного v3.0.4):

from django.db.models import F

MyModel.objects.values(renamed_value=F('cryptic_value_name'))

Выдержка из документации Django :

Объект F () представляет значение поля модели или аннотированного столбца. Это позволяет ссылаться на значения полей модели и выполнять операции с базой данных с их помощью без необходимости извлекать их из базы данных в память Python.

Арпит Сингх
источник
5
Для нескольких значений вы можете использовать:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere
1
но что, если вы хотите изменить одно значение и оставить другое? С аннотацией это возможно: inv.annotate(name=F('stock__name')).values('name', "price")но я не понимаю, как этот метод должен работать в таком случае. inv.values(name=F("stock__name"), price=F("price"))-> Аннотация «цена» конфликтует с полем модели
Мальте
1
@Malte Думаю, не стоит это усложнять. Просто попробуй inv.values('price', name=F("stock__name")). В python вы можете помещать именованные аргументы только после безымянных.
Арпит Сингх,
0

Я работаю с django 1.11.6

(И пара ключ: значение противоположна принятому ответу)

Вот как я заставляю это работать в моем проекте

def json(university):
    address = UniversityAddress.objects.filter(university=university)
    address = address.extra(select={'city__state__country__name': 'country', 'city__state__name': 'state', 'city__name': 'city'})
    address = address.values('country', 'state', "city", 'street', "postal_code").get()
    return address

Обратите внимание, что добавление одновременных объектов objects.filter (). Extra (). Values ​​() такое же, как указано выше.

Судип Бхаттарай
источник
вы можете видеть, что у .extra(select={cryptic_value:ranamed_value})него противоположный ключ: значение по сравнению с принятым ответом
Судип Бхаттарай,
-6

Это более чем просто, если вы хотите переименовать несколько полей режима.

Пытаться

projects = Project.objects.filter()
projects = [{'id': p.id, 'name': '%s (ID:%s)' % (p.department, p.id)} for p in projects]

Здесь у меня нет nameполя в таблице, но я могу получить его после небольшой настройки.

AJ
источник
10
–1 он оценивает набор запросов, что нарушает условия сделки
wim