diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 5569913c6f..229232445b 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -101,6 +101,9 @@ class Manager(object): def values(self, *args, **kwargs): return self.get_query_set().values(*args, **kwargs) + def valueslist(self, *args, **kwargs): + return self.get_query_set().valueslist(*args, **kwargs) + def update(self, *args, **kwargs): return self.get_query_set().update(*args, **kwargs) diff --git a/django/db/models/query.py b/django/db/models/query.py index 7fa49db910..6e7d463e9e 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -278,6 +278,16 @@ class _QuerySet(object): def values(self, *fields): return self._clone(klass=ValuesQuerySet, setup=True, _fields=fields) + def valueslist(self, *fields, **kwargs): + flat = kwargs.pop('flat', False) + if kwargs: + raise TypeError('Unexpected keyword arguments to valueslist: %s' + % (kwargs.keys(),)) + if flat and len(fields) > 1: + raise TypeError("'flat' is not valid when valueslist is called with more than one field.") + return self._clone(klass=ValuesListQuerySet, setup=True, flat=flat, + _fields=fields) + def dates(self, field_name, kind, order='ASC'): """ Returns a list of datetime objects representing all available dates @@ -531,6 +541,21 @@ class ValuesQuerySet(QuerySet): c._setup_query() return c +class ValuesListQuerySet(ValuesQuerySet): + def iterator(self): + self.field_names.extend([f for f in self.query.extra_select.keys()]) + if self.flat and len(self._fields) == 1: + for row in self.query.results_iter(): + yield row[0] + else: + for row in self.query.results_iter(): + yield row + + def _clone(self, *args, **kwargs): + clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs) + clone.flat = self.flat + return clone + class DateQuerySet(QuerySet): def iterator(self): return self.query.results_iter() diff --git a/docs/db-api.txt b/docs/db-api.txt index 61bfd31882..74adc10457 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -664,6 +664,34 @@ followed (optionally) by any output-affecting methods (such as ``values()``), but it doesn't really matter. This is your chance to really flaunt your individualism. +``valueslist(*fields)`` +~~~~~~~~~~~~~~~~~~~~~~~ + +**New in Django development version** + +This is similar to ``values()`` except that instead of returning a list of +dictionaries, it returns a list of tuples. Each tuple contains the value from +the respective field passed into the ``valueslist()`` call -- so the first +item is the first field, etc. For example:: + + >>> Entry.objects.valueslist('id', 'headling') + [(1, u'First entry'), ...] + +If you only pass in a single field, you can also pass in the ``flat`` +parameter. If ``True``, this will mean the returned results are single values, +rather than one-tuples. An example should make the difference clearer:: + + >>> Entry.objects.valueslist('id').order_by('id') + [(1,), (2,), (3,), ...] + + >>> Entry.objects.valueslist('id', flat=True).order_by('id') + [1, 2, 3, ...] + +It is an error to pass in ``flat`` when there is more than one field. + +If you don't pass any values to ``valueslist()``, it will return all the +fields in the model, in the order they were declared. + ``dates(field, kind, order='ASC')`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index d581947498..d21ffba19d 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -165,6 +165,23 @@ FieldDoesNotExist: Article has no field named 'id_plus_two' >>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}] True +# valueslist() is similar to values(), except that the results are returned as +# a list of tuples, rather than a list of dictionaries. Within each tuple, the +# order of the elemnts is the same as the order of fields in the valueslist() +# call. +>>> Article.objects.valueslist('headline') +[(u'Article 5',), (u'Article 6',), (u'Article 4',), (u'Article 2',), (u'Article 3',), (u'Article 7',), (u'Article 1',)] + +>>> Article.objects.valueslist('id').order_by('id') +[(1,), (2,), (3,), (4,), (5,), (6,), (7,)] +>>> Article.objects.valueslist('id', flat=True).order_by('id') +[1, 2, 3, 4, 5, 6, 7] + +>>> Article.objects.valueslist('id', 'headline', flat=True) +Traceback (most recent call last): +... +TypeError: 'flat' is not valid when valueslist is called with more than one field. + # Every DateField and DateTimeField creates get_next_by_FOO() and # get_previous_by_FOO() methods. # In the case of identical date values, these methods will use the ID as a