Comment renommer des éléments dans values ​​() dans Django?

102

Je veux faire à peu près la même chose que dans ce ticket sur djangoproject.com , mais avec un formatage supplémentaire. À partir de cette requête

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

Je veux obtenir quelque chose comme ça:

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

Existe-t-il une autre manière plus intégrée ou dois-je le faire manuellement?

Martin
la source

Réponses:

72

C'est un peu hacky, mais vous pouvez utiliser la extraméthode:

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

Cela fait essentiellement SELECT cryptic_value_name AS renamed_valuedans le SQL.

Une autre option, si vous voulez toujours la version renommée mais que la base de données a le nom cryptique, consiste à nommer votre champ avec le nouveau nom mais à utiliser db_columnpour faire référence au nom d'origine dans la base de données.

Daniel Roseman
la source
6
Apparemment, dans la version 1.7. *, Vous pourrez simplement écrire des noms alias directement en tant qu'arguments nommés values(). Source: code.djangoproject.com/ticket/16735
gregoltsov
19
-1 parce que .values(supports__through_tables) et ce hack ne le fait pas (d'ailleurs probablement le cas d'utilisation le plus évident pour vouloir renommer les ValuesQuerySetclés dict)
wim
1
Quelqu'un a-t-il trouvé un moyen de faire cela à travers des tables (.values ​​(supports__through_tables))?
François Constant
12
Malheureusement, cette solution ne prend pas en charge les noms associés comme dans .values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha
13
Model.objects.annotate (my_alias = F ( 'some__long__name__to__alias')) VALUES ( 'my_alias') du billet 16735..
eugène
188

De django>=1.8vous pouvez utiliser annoter et objet F

from django.db.models import F

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

Va également extra()être obsolète, à partir de la documentation django:

Il s'agit d'une ancienne API que nous visons à rendre obsolète à un moment donné dans le futur. Utilisez-le uniquement si vous ne pouvez pas exprimer votre requête à l'aide d'autres méthodes de jeu de requêtes. Si vous avez besoin de l'utiliser, veuillez déposer un ticket en utilisant le mot-clé QuerySet.extra avec votre cas d'utilisation (veuillez d'abord vérifier la liste des tickets existants) afin que nous puissions améliorer l'API QuerySet pour permettre la suppression de extra (). Nous n'améliorons ni ne corrigeons plus de bogues pour cette méthode.

alejandrodnm
la source
3
Cette réponse ne fonctionne que sur django>=1.8. Les expressions de requête sont activées annotatepour accepter des expressions autres que des agrégats. Référence: docs.djangoproject.com/en/1.9/releases/1.8/…
Yeo
3
Cela fonctionne, mais la syntaxe proposée dans la question originale n'est-elle pas tellement plus agréable ? MyModel.objects.values(renamed_value='cryptic_value_name')
wim
11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))works
jarcoal
1
Cette réponse fonctionne mais est redondante. Les arguments de mot-clé transmis à values()sont transmis annotate()en votre nom (voir docs.djangoproject.com/en/2.2/ref/models/querysets/… ). La réponse MyModel.objects.values(renamed_value=F('cryptic_value_name'))est plus simple et plus claire, IMO.
tobias.mcnulty
76

Sans utiliser aucune autre méthode de gestion (testée sur v3.0.4):

from django.db.models import F

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

Extrait de la documentation Django :

Un objet F () représente la valeur d'un champ de modèle ou d'une colonne annotée. Il permet de faire référence aux valeurs de champ de modèle et d'effectuer des opérations de base de données en les utilisant sans avoir à les extraire de la base de données dans la mémoire Python.

Arpit Singh
la source
5
Pour plus d'une valeur, vous pouvez utiliser:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere
1
mais que faire si vous voulez changer une valeur et conserver l'autre? Avec annoter c'est possible: inv.annotate(name=F('stock__name')).values('name', "price")mais je ne vois pas comment cette méthode devrait fonctionner dans ce cas. inv.values(name=F("stock__name"), price=F("price"))-> L'annotation 'prix' entre en conflit avec un champ sur le modèle
Malte
1
@Malte Je pense qu'il n'est pas nécessaire de compliquer cela. Essayez juste inv.values('price', name=F("stock__name")). En python, vous ne pouvez placer des arguments nommés qu'après ceux sans nom.
Arpit Singh le
0

Je travaille avec django 1.11.6

(Et la paire clé: valeur est opposée à celle de la réponse acceptée)

C'est ainsi que je le fais fonctionner pour mon projet

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

Notez que l'ajout simultané de objects.filter (). Extra (). Values ​​() est identique à ci-dessus.

Sudip Bhattarai
la source
vous pouvez voir que la .extra(select={cryptic_value:ranamed_value})clé est opposée: la valeur par rapport à la réponse acceptée
Sudip Bhattarai
-6

C'est plus que simple si vous souhaitez renommer quelques champs du mode.

Essayer

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

Ici, je n'ai pas de namechamp dans le tableau, mais je peux l'obtenir après avoir peaufiné un peu.

UN J
la source
10
–1 il évalue l'ensemble de requêtes, qui est un
facteur décisif