Jak zmienić nazwy elementów w values ​​() w Django?

102

Chcę zrobić prawie to samo, co na tym bilecie na djangoproject.com , ale z dodatkowym formatowaniem. Z tego zapytania

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

Chcę dostać coś takiego:

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

Czy jest inny, bardziej wbudowany sposób, czy też muszę to zrobić ręcznie?

Jaskółka oknówka
źródło

Odpowiedzi:

72

To trochę hacky, ale możesz użyć extrametody:

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

Zasadniczo dzieje się to SELECT cryptic_value_name AS renamed_valuew SQL.

Inną opcją, jeśli zawsze chcesz zmienić nazwę wersji, ale baza danych ma tajemniczą nazwę, to nazwanie pola nową nazwą, ale użyj, db_columnaby odwołać się do oryginalnej nazwy w bazie danych.

Daniel Roseman
źródło
6
Najwyraźniej w 1.7. * Będziesz mógł po prostu zapisywać aliasy bezpośrednio jako nazwane argumenty values(). Źródło: code.djangoproject.com/ticket/16735
gregoltsov
19
-1 ponieważ .values(supports__through_tables)i ten hack nie (nawiasem mówiąc, prawdopodobnie najbardziej oczywisty przypadek użycia zmiany nazwy ValuesQuerySetklawiszy dyktowania)
wim
1
Czy ktoś znalazł sposób, aby to zrobić za pomocą tabel (.values ​​(support__through_tables))?
François Constant
12
Niestety to rozwiązanie nie obsługuje nazw pokrewnych, takich jak w .values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha
13
Model.objects.annotate (my_alias = F ('some__long__name__to__alias')). Values ​​('my_alias') z biletu 16735.
eugene
188

Od django>=1.8można użyć opisywanie i przedmiot F

from django.db.models import F

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

Również extra()zostanie wycofane z dokumentacji django:

Jest to stary interfejs API, który chcemy w przyszłości wycofać. Użyj go tylko wtedy, gdy nie możesz wyrazić zapytania przy użyciu innych metod zestawu zapytań. Jeśli musisz go użyć, prześlij zgłoszenie za pomocą słowa kluczowego QuerySet.extra w swoim przypadku użycia (najpierw sprawdź listę istniejących biletów), abyśmy mogli ulepszyć QuerySet API, aby umożliwić usunięcie extra (). Nie ulepszamy już ani nie naprawiamy błędów tej metody.

alejandrodnm
źródło
3
Ta odpowiedź działa tylko na django>=1.8. Wyrażenia zapytań annotatemogą akceptować wyrażenia inne niż agregaty. Źródła
Yeo,
3
Działa, ale czy składnia zaproponowana w pierwotnym pytaniu nie jest o wiele ładniejsza ? MyModel.objects.values(renamed_value='cryptic_value_name')
wim
11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))działa
jarcoal
1
Ta odpowiedź działa, ale jest zbędna. Przekazywane argumenty słów kluczowych values()są przekazywane annotate()w Twoim imieniu (patrz docs.djangoproject.com/en/2.2/ref/models/querysets/ ... ). Odpowiedź MyModel.objects.values(renamed_value=F('cryptic_value_name'))jest prostsza i bardziej przejrzysta, IMO.
tobias.mcnulty
76

Bez użycia innej metody menedżera (przetestowane v3.0.4):

from django.db.models import F

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

Fragment z dokumentacji Django :

Obiekt F () reprezentuje wartość pola modelu lub kolumny z adnotacjami. Umożliwia odwoływanie się do wartości pól modelu i wykonywanie operacji na bazie danych przy ich użyciu bez konieczności wyciągania ich z bazy danych do pamięci Pythona.

Arpit Singh
źródło
5
Dla więcej niż jednej wartości możesz użyć:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere
1
ale co, jeśli chcesz zmienić jedną wartość i zachować drugą? Z adnotacją jest to możliwe: inv.annotate(name=F('stock__name')).values('name', "price")ale nie widzę, jak ta metoda powinna działać w takim przypadku. inv.values(name=F("stock__name"), price=F("price"))-> Adnotacja „cena” koliduje z polem na modelu
Malte
1
@Malte Myślę, że nie ma potrzeby tego komplikować. Po prostu spróbuj inv.values('price', name=F("stock__name")). W Pythonie możesz umieścić nazwane argumenty tylko po nienazwanych argumentach.
Arpit Singh
0

Pracuję z django 1.11.6

(A klucz: para wartości jest odwrotna do akceptowanej odpowiedzi)

W ten sposób pracuję nad moim projektem

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

Zwróć uwagę, że dodawanie jednoczesnych obiektów .filter (). Extra (). Wartości () jest takie samo jak powyżej.

Sudip Bhattarai
źródło
widać, że .extra(select={cryptic_value:ranamed_value})ma odwrotny klucz: wartość w porównaniu z zaakceptowaną odpowiedzią
Sudip Bhattarai
-6

To więcej niż proste, jeśli chcesz zmienić nazwę kilku pól trybu.

Próbować

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

Tutaj nie mam namepola w tabeli, ale mogę to uzyskać po drobnych poprawkach.

AJ
źródło
10
–1 ocenia zestaw zapytań, który jest przełomem
wim