Como renomear itens em values ​​() no Django?

101

Eu quero fazer praticamente o mesmo como neste tíquete em djangoproject.com , mas com alguma formatação adicional. Desta consulta

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

Eu quero algo assim:

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

Existe outra maneira mais embutida ou tenho que fazer isso manualmente?

Martin
fonte

Respostas:

72

É um pouco hacky, mas você pode usar o extramétodo:

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

Isso basicamente faz SELECT cryptic_value_name AS renamed_value no SQL.

Outra opção, se você sempre deseja a versão renomeada, mas o banco de dados tem o nome críptico, é nomear seu campo com o novo nome, mas usar db_columnpara se referir ao nome original no banco de dados.

Daniel Roseman
fonte
6
Aparentemente, no 1.7. * Você poderá escrever nomes com alias diretamente como argumentos nomeados para values(). Fonte: code.djangoproject.com/ticket/16735
gregoltsov
19
-1 porque .values(supports__through_tables)e este hack não (aliás, provavelmente o caso de uso mais óbvio para querer renomear as ValuesQuerySetchaves de dicionário)
wim
1
Alguém encontrou uma maneira de fazer isso por meio de tabelas (.values ​​(supported__through_tables))?
François Constant,
12
Infelizmente, esta solução não oferece suporte a nomes relacionados, como em .values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha
13
Model.objects.annotate (my_alias = F ('some__long__name__to__alias')). Values ​​('my_alias') do tíquete 16735.
eugene
188

De django>=1.8você pode usar anotar e objeto F

from django.db.models import F

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

Também extra()será descontinuado, a partir dos documentos do django:

Esta é uma API antiga que pretendemos descontinuar em algum momento no futuro. Use-o apenas se você não puder expressar sua consulta usando outros métodos do queryset. Se precisar usá-lo, preencha um tíquete usando a palavra-chave QuerySet.extra com seu caso de uso (verifique a lista de tíquetes existentes primeiro) para que possamos aprimorar a API QuerySet para permitir a remoção de extras (). Não estamos mais melhorando ou corrigindo bugs para este método.

alejandrodnm
fonte
3
Esta resposta só funciona django>=1.8. Expressões de consulta habilitadas annotatepara aceitar expressões diferentes de agregados. Referência: docs.djangoproject.com/en/1.9/releases/1.8/…
Yeo
3
Funciona, mas a sintaxe proposta na pergunta original não é muito melhor ? MyModel.objects.values(renamed_value='cryptic_value_name')
wim
11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))obras
jarcoal
1
Essa resposta funciona, mas é redundante. Argumentos de palavra- chave passados ​​para values()são passados annotate()em seu nome (consulte docs.djangoproject.com/en/2.2/ref/models/querysets/… ). A resposta com MyModel.objects.values(renamed_value=F('cryptic_value_name'))é mais simples e mais clara, IMO.
tobias.mcnulty
76

Sem usar qualquer outro método de gerenciamento (testado em v3.0.4):

from django.db.models import F

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

Trecho da documentação do Django :

Um objeto F () representa o valor de um campo de modelo ou coluna anotada. Torna possível referir-se aos valores de campo do modelo e realizar operações de banco de dados usando-os sem realmente ter que retirá-los do banco de dados para a memória Python.

Arpit Singh
fonte
5
Para mais de um valor você pode usar:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere
1
mas e se você quiser alterar um valor e manter o outro? Com anotação é possível: inv.annotate(name=F('stock__name')).values('name', "price")mas não vejo como esse método deveria funcionar nesse caso. inv.values(name=F("stock__name"), price=F("price"))-> A anotação 'preço' entra em conflito com um campo no modelo
Malte
1
@Malte acho que não precisa complicar isso. Apenas tente inv.values('price', name=F("stock__name")). Em python, você só pode colocar argumentos nomeados após os não nomeados.
Arpit Singh
0

Estou trabalhando com Django 1.11.6

(E o par chave: valor é oposto ao da resposta aceita)

É assim que estou fazendo funcionar para o meu projeto

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

Observe que adicionar objetos simultâneos. Filtro (). Extra (). Valores () é igual ao anterior.

Sudip Bhattarai
fonte
você pode ver que .extra(select={cryptic_value:ranamed_value})tem a chave oposta: valor comparado à resposta aceita
Sudip Bhattarai
-6

É mais do que simples se você deseja renomear alguns campos do modo.

Experimentar

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

Aqui, eu não tenho um namecampo na tabela, mas posso conseguir isso depois de alguns ajustes.

AJ
fonte
10
–1 avalia o queryset, que é um disjuntor
wim