diff --git a/django/db/models/query.py b/django/db/models/query.py index b2ed0d36a7..9a590ddd26 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -391,17 +391,22 @@ class ValuesQuerySet(QuerySet): # 'fields' is used to configure the query, whilst field_names is stored # in this object for use by iterator(). if self._fields: + opts = self.model._meta + all = dict([(field.column, field) for field in opts.fields]) + all.update([(field.name, field) for field in opts.fields]) if not self.query.extra_select: - fields = [self.model._meta.get_field(f, many_to_many=False) - for f in self._fields] + try: + fields = [all[f] for f in self._fields] + except KeyError, e: + raise FieldDoesNotExist('%s has no field named %r' + % (opts.object_name, e.args[0])) field_names = self._fields else: fields = [] field_names = [] for f in self._fields: - if f in [field.name for field in self.model._meta.fields]: - fields.append(self.model._meta.get_field(f, - many_to_many=False)) + if f in all: + fields.append(all[f]) field_names.append(f) elif not self.query.extra_select.has_key(f): raise FieldDoesNotExist('%s has no field named %r' diff --git a/docs/db-api.txt b/docs/db-api.txt index 2340c32ce4..3906376f47 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -583,6 +583,30 @@ Example:: >>> Blog.objects.values('id', 'name') [{'id': 1, 'name': 'Beatles Blog'}] +A couple of subtleties that are worth mentioning: + + * The ``values()`` method does not return anything for ``ManyToManyField`` + attributes and will raise an error if you try to pass in this type of + field to it. + * If you have a field called ``foo`` that is a ``ForeignKey``, the default + ``values()`` call will return a dictionary key called ``foo_id``, since + this is the name of the hidden model attribute that stores the actual + value (the ``foo`` attribute refers to the related model). When you are + calling ``values()`` and passing in field names, you can pass in either + ``foo`` or ``foo_id`` and you will get back the same thing (the + dictionary key will match the field name you passed in). + + For example:: + + >>> Entry.objects.values() + [{'blog_id: 1, 'headline': u'First Entry', ...}, ...] + + >>> Entry.objects.values('blog') + [{'blog': 1}, ...] + + >>> Entry.objects.values('blog_id') + [{'blog_id': 1}, ...] + A ``ValuesQuerySet`` is useful when you know you're only going to need values from a small number of the available fields and you won't need the functionality of a model instance object. It's more efficient to select only diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 9b076633c7..6748950f21 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -334,5 +334,24 @@ Bug #3037 Bug #5321 >>> Note.objects.values('misc').distinct().order_by('note', '-misc') [{'misc': u'foo'}, {'misc': u'bar'}] + +Bug #4358 +If you don't pass any fields to values(), relation fields are returned as +"foo_id" keys, not "foo". For consistency, you should be able to pass "foo_id" +in the fields list and have it work, too. We actually allow both "foo" and +"foo_id". + +# The *_id version is returned by default. +>>> 'note_id' in ExtraInfo.objects.values()[0] +True + +# You can also pass it in explicitly. +>>> ExtraInfo.objects.values('note_id') +[{'note_id': 1}, {'note_id': 2}] + +# ...or use the field name. +>>> ExtraInfo.objects.values('note') +[{'note': 1}, {'note': 2}] + """}