298

In Django, what's the difference between the following two:

Article.objects.values_list('comment_id', flat=True).distinct()

versus:

Article.objects.values('comment_id').distinct()

My goal is to get a list of unique comment ids under each Article. I've read the documentation (and in fact have used both approaches). The results overtly seem similar.

Hassan Baig
  • 15,055
  • 27
  • 102
  • 205
  • With values_list you can do `if self.id in Article.objects.values_list('comment_id', flat=True):` while using values you need to access the dictionary – dnaranjo May 13 '16 at 09:20
  • 1
    @dnaranjo - You could but why not just do `Article.objects.filter(comment_id=self.id).exists()`? – Sayse May 13 '16 at 09:34
  • 2
    That's an answer for a different question – dnaranjo May 13 '16 at 10:17

5 Answers5

474

The values() method returns a QuerySet containing dictionaries:

<QuerySet [{'comment_id': 1}, {'comment_id': 2}]>

The values_list() method returns a QuerySet containing tuples:

<QuerySet [(1,), (2,)]>

If you are using values_list() with a single field, you can use flat=True to return a QuerySet of single values instead of 1-tuples:

<QuerySet [1, 2]>
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • Oh and no difference among the two vis-a-vis how `distinct()` is used huh? – Hassan Baig May 13 '16 at 09:16
  • 3
    No, I don't think `distinct()` works any differently. The important thing is which data structure that you want to work with. – Alasdair May 13 '16 at 09:18
  • 5
    The `values()` returns a `QuerySet` and not a `list`. Although the object returned by `values()` looks like a `list`, it doesn't behave like one in some cases. For example, it won't be json serializable unless we convert it into a `list' – Abhijit Ghate Aug 07 '18 at 11:57
  • @AbhijitGhate good point, I've updated the answer to make that clearer. – Alasdair Aug 07 '18 at 12:20
  • 5
    You can easily convert the return from `values_list` to a true Python list by just using the `list` function: `list(Article.objects.values_list('comment_id', flat=True).distinct())` – inostia May 13 '19 at 23:08
  • As mentioned, if your tuple has 1 value then you can use `flat=True`. But if it has 2 values such as `[('name', 'Jim')]` you can use `dict(`[('name', 'Jim')]`)` which converts it to a dictionary with key-value pairs: `{'name': 'Jim'}`. Very useful. – enchance Feb 16 '20 at 16:08
  • @inostia In Django 2.2 I get "Error in argument:" when trying to convert using `list()` – l0b0 Jun 18 '20 at 06:24
  • @l0b0 the issue is probably in the queryset itself, not the `list()` call. We can’t help with that in the comments here. If you’re still sick please open a new question and include the code, models and full traceback of the error. – Alasdair Jun 18 '20 at 08:14
  • Turns out `set()` does work, so I'm just using that. – l0b0 Jun 18 '20 at 09:22
104

values()

Returns a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.

values_list()

Returns a QuerySet that returns list of tuples, rather than model instances, when used as an iterable.

distinct()

distinct are used to eliminate the duplicate elements.

Example:

>>> list(Article.objects.values_list('id', flat=True)) # flat=True will remove the tuples and return the list   
[1, 2, 3, 4, 5, 6]

>>> list(Article.objects.values('id'))
[{'id':1}, {'id':2}, {'id':3}, {'id':4}, {'id':5}, {'id':6}]
Flimm
  • 136,138
  • 45
  • 251
  • 267
Ibrahim Kasim
  • 1,414
  • 1
  • 10
  • 9
8

"values()" returns a QuerySet of dictionaries.

For example:

print(User.objects.all().values()) # Return all fields
# <QuerySet [{'id': 1, 'name': 'John'}, {'id': 2, 'name': 'Tom'}]>

print(User.objects.all().values("name")) # Return "name" field
# <QuerySet [{'name': 'John'}, {'name': 'Tom'}]>

"values_list()" returns a QuerySet of tuples.

For example:

print(User.objects.all().values_list()) # Return all fields
# <QuerySet [(1, 'John'), (2, 'Tom')]>

print(User.objects.all().values_list("name")) # Return "name" field
# <QuerySet [('John',), ('Tom',)]>

"values_list()" with "flat=True" returns a QuerySet of values. *No or One field with "flat=True" is allowed and one field must be the 1st argument with "flat=True" which must be the 2nd argument.

For example:

print(User.objects.all().values_list(flat=True)) # Return "id" field
# <QuerySet [1, 2]>

print(User.objects.all().values_list("name", flat=True)) # Return "name" field
# <QuerySet ['John', 'Tom']>

print(User.objects.all().values_list(flat=True, "name")) # Error

print(User.objects.all().values_list("id", "name", flat=True)) # Error
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
5

You can get the different values with:

set(Article.objects.values_list('comment_id', flat=True))
glennsl
  • 28,186
  • 12
  • 57
  • 75
pitu
  • 67
  • 1
  • 1
1

The best place to understand the difference is at the official documentation on values / values_list. It has many useful examples and explains it very clearly. The django docs are very user freindly.

Here's a short snippet to keep SO reviewers happy:

values

Returns a QuerySet that returns dictionaries, rather than model instances, when used as an iterable.

And read the section which follows it:

value_list

This is similar to values() except that instead of returning dictionaries, it returns tuples when iterated over.

run_the_race
  • 1,344
  • 2
  • 36
  • 62