¿Cómo cambiar el nombre de los elementos en los valores () en Django?

101

Quiero hacer más o menos lo mismo que en este ticket en djangoproject.com , pero con un formato adicional. De esta consulta

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

Quiero conseguir algo como eso:

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

¿Existe otra forma más integrada o tengo que hacerlo manualmente?

Martín
fuente

Respuestas:

72

Es un poco hacky, pero podrías usar el extramétodo:

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

Esto básicamente lo hace SELECT cryptic_value_name AS renamed_valueen SQL.

Otra opción, si siempre desea la versión renombrada pero la base de datos tiene el nombre críptico, es nombrar su campo con el nuevo nombre pero usar db_columnpara referirse al nombre original en la base de datos.

Daniel Roseman
fuente
6
Aparentemente, en 1.7. * Podrá escribir nombres con alias directamente como argumentos con nombre values(). Fuente: code.djangoproject.com/ticket/16735
gregoltsov
19
-1 porque .values(supports__through_tables)y este truco no lo hace (por cierto, probablemente el caso de uso más obvio para querer cambiar el nombre de las ValuesQuerySetclaves de
dictado
1
¿Alguien ha encontrado una manera de hacer esto a través de tablas (.values ​​(supports__through_tables))?
François Constant
12
Desafortunadamente, esta solución no admite nombres relacionados como en .values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha
13
Model.objects.annotate (my_alias = F ( 'some__long__name__to__alias')) valores ( 'my_alias') desde el billete 16735..
Eugene
188

Desde django>=1.8puede usar anotar y objeto F

from django.db.models import F

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

También extra()estará obsoleto, de los documentos de django:

Esta es una API antigua que pretendemos desaprobar en algún momento en el futuro. Úselo solo si no puede expresar su consulta utilizando otros métodos de conjunto de consultas. Si necesita usarlo, presente un ticket usando la palabra clave QuerySet.extra con su caso de uso (primero verifique la lista de tickets existentes) para que podamos mejorar la API de QuerySet para permitir la eliminación de extra (). Ya no estamos mejorando ni solucionando errores de este método.

alejandrodnm
fuente
3
Esta respuesta solo funciona django>=1.8. Expresiones de consulta habilitadas annotatepara aceptar expresiones distintas de agregados. Referencia: docs.djangoproject.com/en/1.9/releases/1.8/…
Yeo
3
Funciona, pero ¿no es mucho mejor la sintaxis propuesta en la pregunta original ? MyModel.objects.values(renamed_value='cryptic_value_name')
wim
11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))obras
jarcoal
1
Esta respuesta funciona pero es redundante. Los argumentos de palabras clave que se pasan a values()se transmiten annotate()en su nombre (consulte docs.djangoproject.com/en/2.2/ref/models/querysets/… ). La respuesta con MyModel.objects.values(renamed_value=F('cryptic_value_name'))es más simple y más clara, en mi opinión.
tobias.mcnulty
76

Sin usar ningún otro método de administrador (probado en v3.0.4):

from django.db.models import F

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

Extracto de los documentos de Django :

Un objeto F () representa el valor de un campo de modelo o columna anotada. Hace posible hacer referencia a los valores de campo del modelo y realizar operaciones de base de datos utilizándolos sin tener que sacarlos de la base de datos a la memoria de Python.

Arpit Singh
fuente
5
Para más de un valor puede utilizar:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere
1
pero ¿y si desea cambiar un valor y mantener el otro? Con anotar es posible: inv.annotate(name=F('stock__name')).values('name', "price")pero no veo cómo debería funcionar este método en ese caso. inv.values(name=F("stock__name"), price=F("price"))-> La anotación 'precio' entra en conflicto con un campo en el modelo
Malte
1
@Malte Creo que no hay necesidad de complicar esto. Solo inténtalo inv.values('price', name=F("stock__name")). En Python, solo puede colocar argumentos con nombre después de los que no tienen nombre.
Arpit Singh
0

Estoy trabajando con django 1.11.6

(Y el par clave: valor es opuesto al de la respuesta aceptada)

Así es como lo hago funcionar para mi proyecto

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

Tenga en cuenta que la adición simultánea de objects.filter (). Extra (). Values ​​() es igual que el anterior.

Sudip Bhattarai
fuente
puede ver que .extra(select={cryptic_value:ranamed_value})tiene la clave opuesta: valor en comparación con la respuesta aceptada
Sudip Bhattarai
-6

Es más que simple si desea cambiar el nombre de algunos campos del modo.

Tratar

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

Aquí no tengo un namecampo en la tabla, pero puedo obtenerlo después de ajustarlo un poco.

AJ
fuente
10
–1 evalúa el conjunto de consultas, que es un
factor decisivo