Django
values_list
values
queryset
Python

Django values_list vs values

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Django ORM, both values() and values_list() let you fetch selected columns without constructing full model instances. They are similar in purpose but return different result shapes, and that difference usually determines which one is the better choice.

What values() Returns

values() returns dictionaries keyed by field name. Each row is self-describing, which makes the result easy to read and convenient for reporting code, serializers, or API-style responses.

python
1from myapp.models import Book
2
3rows = Book.objects.values("id", "title", "published_at")
4
5for row in rows:
6    print(row["id"], row["title"], row["published_at"])

This shape is especially useful when:

  • Field names matter to the consumer.
  • You want JSON-like output.
  • The selected fields may evolve and you want explicit keys at call sites.

It is also friendlier in templates, serializers, and reporting code where named access is clearer than positional tuple access.

What values_list() Returns

values_list() returns tuples instead of dictionaries. The values appear in the same order as the fields you requested.

python
1from myapp.models import Book
2
3rows = Book.objects.values_list("id", "title")
4
5for book_id, title in rows:
6    print(book_id, title)

This is usually a better fit when:

  • You only need positional data.
  • You want less overhead than dictionaries.
  • The result feeds tuple-based operations or unpacking logic.

For a single field, flat=True gives a simple one-dimensional queryset.

python
titles = Book.objects.values_list("title", flat=True)
print(list(titles))

flat=True only works when you ask for exactly one field, but it is extremely convenient when you need a plain list of ids, emails, slugs, or similar values.

Practical Difference in Calling Code

The main difference becomes obvious in the calling code. values() favors clarity through named access. values_list() favors compactness through positional access.

With values():

python
1authors = Book.objects.values("id", "author__name")
2
3for row in authors:
4    print(f'Book {row["id"]} by {row["author__name"]}')

With values_list():

python
1authors = Book.objects.values_list("id", "author__name")
2
3for book_id, author_name in authors:
4    print(f"Book {book_id} by {author_name}")

The tuple form is concise when the selected columns are obvious. The dictionary form is safer when the result has many fields or several joined columns.

Performance and Memory Considerations

Both methods are lighter than retrieving full model objects because Django skips instance construction. Between the two, values_list() is usually a little leaner because tuples are simpler than dictionaries.

That difference matters mainly when:

  • You query many rows.
  • You select only a few columns.
  • The result is part of a tight batch job.

In ordinary application code, readability should usually matter more than micro-optimizing the shape.

Use Cases That Fit values_list() Better

Some ORM operations become especially convenient with values_list().

Example collecting ids for a second query:

python
active_ids = User.objects.filter(is_active=True).values_list("id", flat=True)
print(list(active_ids))

This is a natural fit because the code wants one column, not a row dictionary.

Another common case is distinct single-field lookups:

python
emails = User.objects.values_list("email", flat=True).distinct()

That is much cleaner than extracting "email" from dictionaries manually.

Use Cases That Fit values() Better

values() is more self-describing for APIs, exports, and tabular transformations.

python
1report_rows = Order.objects.values(
2    "id",
3    "customer__name",
4    "status",
5    "total_amount",
6)
7
8for row in report_rows:
9    print(row)

When the selected fields become numerous, positional tuple unpacking becomes harder to follow. Named keys remain readable.

Be Careful with Annotations and Aggregation

Both methods work well with annotate(), but the result shape still matters.

python
1from django.db.models import Count
2
3stats = Author.objects.values("country").annotate(book_count=Count("book"))
4
5for row in stats:
6    print(row["country"], row["book_count"])

You can use values_list() here too, but it forces you to remember tuple ordering exactly, which becomes error-prone as queries grow more complex.

Common Pitfalls

  • Using flat=True with more than one selected field.
  • Choosing values_list() and later forgetting the tuple field order.
  • Choosing values() for one simple field when a flat list would be cleaner.
  • Assuming either method returns full model behavior such as property methods or model methods.
  • Refactoring selected fields without updating tuple unpacking code.

Summary

  • 'values() returns dictionaries and is best when named access improves clarity.'
  • 'values_list() returns tuples and is best when positional access is sufficient.'
  • 'values_list(..., flat=True) is ideal for one-column results.'
  • Both are lighter than loading full model instances.
  • Prefer readability first, then optimize with profiling if the query is on a hot path.

Course illustration
Course illustration

All Rights Reserved.