175

I want to do pretty much the same like in this ticket at djangoproject.com, but with some additonal formatting. From this query

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

I want to get something like that:

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

Is there another, more builtin way or do I have to do this manually?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Martin
  • 4,170
  • 6
  • 30
  • 47

6 Answers6

280

From django>=1.8 you can use annotate and F object

from django.db.models import F

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

Also extra() is going to be deprecated, from the django docs:

This is an old API that we aim to deprecate at some point in the future. Use it only if you cannot express your query using other queryset methods. If you do need to use it, please file a ticket using the QuerySet.extra keyword with your use case (please check the list of existing tickets first) so that we can enhance the QuerySet API to allow removing extra(). We are no longer improving or fixing bugs for this method.

alejandrodnm
  • 5,410
  • 3
  • 26
  • 28
  • 3
    This answer only work on `django>=1.8`. Query Expressions enabled `annotate` to accept expressions other than aggregates. Reference: https://docs.djangoproject.com/en/1.9/releases/1.8/#query-expressions-conditional-expressions-and-database-functions – Yeo Jan 27 '16 at 06:48
  • 3
    It works, but isn't the syntax proposed in the original question just **so much nicer**? `MyModel.objects.values(renamed_value='cryptic_value_name')` – wim Jul 27 '16 at 15:08
  • 28
    `MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))` works – jarcoal Jun 21 '17 at 16:50
  • 4
    This answer works but is redundant. Keyword arguments passed to `values()` are passed through `annotate()` on your behalf (see https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.query.QuerySet.values). The answer with `MyModel.objects.values(renamed_value=F('cryptic_value_name'))` is simpler and more clear, IMO. – tobias.mcnulty Jul 04 '19 at 15:58
159

Without using any other manager method (tested on v3.0.4):

from django.db.models import F

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

Excerpt from Django docs:

An F() object represents the value of a model field or annotated column. It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory.

Arpit Singh
  • 3,387
  • 2
  • 17
  • 11
  • 9
    For more than one value you can use: ``MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))`` – alpere Jun 16 '19 at 15:17
  • 1
    but what if you want to **change** one value and **keep** the other one? With annotate it is possible: `inv.annotate(name=F('stock__name')).values('name', "price")` but I don't see how this method should work in that case. `inv.values(name=F("stock__name"), price=F("price"))` -> The annotation 'price' conflicts with a field on the model – DeemonRider Apr 08 '20 at 22:47
  • 7
    @Malte I think there is not need to complicate this. Just try `inv.values('price', name=F("stock__name"))`. In python you can only put named arguments after unnamed ones. – Arpit Singh Apr 11 '20 at 12:16
  • Is it possible to set up a new column name with spaces in this approach? E.g. "Renamed value" instead of renamed_value. Can't pass a string as argument name. – Azzonith May 02 '22 at 15:53
  • 2
    @Azzonith you can use keyword arguments like `MyModel.objects.values(**{'Renamed value': F('cryptic_value_name')})` – Arpit Singh Jun 29 '22 at 20:14
84

It's a bit hacky, but you could use the extra method:

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

This basically does SELECT cryptic_value_name AS renamed_value in the SQL.

Another option, if you always want the renamed version but the db has the cryptic name, is to name your field with the new name but use db_column to refer to the original name in the db.

Community
  • 1
  • 1
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 7
    Apparently in 1.7.* you'll be able to just write aliased names straight as named arguments to `values()`. Source: https://code.djangoproject.com/ticket/16735 – gregoltsov Jul 07 '14 at 16:31
  • Not according to that: https://code.djangoproject.com/ticket/16735#comment:18 and this https://docs.djangoproject.com/en/1.7/releases/1.7/ – chaim Aug 18 '14 at 01:33
  • 26
    -1 because `.values(supports__through_tables)` and this hack doesn't (incidentally probably the most obvious use case for wanting to rename the `ValuesQuerySet` dict keys) – wim Aug 21 '14 at 13:12
  • 1
    Has anyone found a way to do this through tables (.values(supports__through_tables))? – François Constant Oct 02 '14 at 06:30
  • 15
    Unfortunately, this solution does not support related names like in `.values('foreignkeymodel__id', 'foreignkeymodel__name')`. – Risadinha Oct 27 '14 at 16:49
  • 16
    Model.objects.annotate(my_alias=F('some__long__name__to__alias')).values('my_alias') from the ticket 16735. – eugene Apr 22 '15 at 09:47
  • does anyone know how to rename multiple items? – mrek Sep 01 '16 at 10:19
  • chaim / gregoltsov That issue was closed: wontfix, but this is in master: https://code.djangoproject.com/ticket/25871 – Aaron McMillin Dec 13 '16 at 18:46
  • 1
    According to the Django 2.0 documentation "extra" will be deprecated soon – Eugene Kovalev Jan 12 '18 at 09:50
  • 1
    As @EugeneKovalev has state, I would change the accepted answer on this to the below. Although a perfectly valid answer, .extra() is described in the Django documentation as `Use this method as a last resort`. See: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#extra It also comes with a security warning: as explained in the docs "You should be very careful whenever you use `extra()`. Every time you use it, you should escape any parameters that the user can control by using params in order to protect against SQL injection attacks." – Micheal J. Roberts Aug 20 '19 at 08:47
1

I am working with django 1.11.6

( And the key:value pair is opposite to that of accepted answer )

This is how i am making it work for my project

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

Note that adding simultanous objects.filter().extra().values() is same as above.

Community
  • 1
  • 1
Sudip Bhattarai
  • 1,082
  • 9
  • 23
0

Try passing as kwargs:

MyModel.objects.annotate(**{'A B C':F('profile_id')}).values('A B C')

In my case, there were spaces and other special characters included in the key of each value in the result set so this did the trick.

-6

Its more than simple if you want to rename few fields of the mode.

Try

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

Here i do not have a name field in the table, but i can get that after tweaking a little bit.

A.J.
  • 8,557
  • 11
  • 61
  • 89