values() and values_list()
coreintermediatevalues("a", "b") returns dicts ({"a": ..., "b": ...}) instead of model instances — no model methods/properties available, but lighter and directly JSON-serializable. values_list("a", "b") returns tuples instead — flat=True (single field only) unwraps 1-tuples into plain values, and named=True returns namedtuples with attribute access. Both are real projections: only the named fields are actually selected in the SQL, not just hidden after the fact.
Think of it as
A normal QuerySet gives back full model instances — every field loaded, every method/property available, at the cost of building real Python objects. values()/values_list() are for the moment none of that object machinery is needed — just the raw data, shaped as a dict or a tuple, which is both cheaper to build and often exactly the shape something else (a JSON API response, a CSV export, a simple lookup) already wants. The choice between values() and values_list() is really about downstream ergonomics: dicts read naturally by key, tuples are more compact and fine when field ORDER is enough context.
What we're doing: Get a flat list of order IDs (for use elsewhere, e.g. a second query's __in filter) without building full Order instances.
- 1
- flat=True with a single field ("id") returns bare ids directly — [1, 5, 12, ...] — rather than [(1,), (5,), (12,)], which is both easier to read and exactly what __in expects on the next line.
Why this works: Building full Order objects here would be wasted work — nothing about status, total, or any other field is needed, only the ids, to feed a second query's __in= lookup. values_list(flat=True) gets exactly that shape directly from the database, without the overhead of instantiating a full model object per row.
Using flat=True with more than one field
Wrong
Better
What you see: TypeError: 'flat' is not valid when values_list is called with more than one field.
Why: flat=True exists specifically to unwrap a SINGLE-field tuple into a bare value — with two or more fields, there is no single value to unwrap into (each row would still need to be a pair), so Django rejects the combination outright rather than silently picking one field to flatten.
- values("a", "b")
- Returns dicts — {"a": ..., "b": ...}
- Directly JSON-serializable
- Read naturally by key
- values_list("a", "b")
- Returns tuples — (a, b)
- flat=True unwraps a single field to bare values
- named=True gives namedtuples, dot-access
values() vs values_list() variants
Together
Remember: values() returns dicts; values_list() returns tuples (flat=True for a single field's bare values; named=True for attribute-accessible namedtuples). Both are real SQL projections — only the requested columns are ever selected — and both skip model-instance construction, the right choice whenever full model objects (with their methods/properties) are not actually needed.
See also: only and defer · grouping with values · retrieval methods

