1.F() and Q() expressions
F("field_name") refers to a field's value inside the database itself, without pulling it into Python — used for race-safe increments (F("count") + 1) and for comparing two fields on the same row. Q(...) wraps a lookup so multiple lookups can be combined with & (AND), | (OR), and ~ (NOT) — needed the moment a filter needs OR logic or NOT logic, which filter()'s own keyword arguments cannot express.
Key features
- F("field") + 1 as an assignment defers the actual addition to the database — reporter.count = F("count") + 1; reporter.save() is race-safe, unlike reporter.count += 1
- After an F()-based save(), the in-memory attribute is NOT the new value — refresh_from_db() (or re-fetching) is required to see the actual updated number
- An F() assignment on an instance PERSISTS — a second .save() call re-applies the same +1 again unless the field is reset first
- Q(a=1) | Q(b=2) is OR; Q(a=1) & Q(b=2) is AND (same as filter(a=1, b=2)); ~Q(a=1) is NOT
- F() can also compare two fields directly in a filter: filter(end_date__lt=F("start_date")) — a comparison the database evaluates per-row
F() vs Q() — what each solves
- F("count") + 1
- Computed inside the database, atomically
- Race-safe — no read-then-write window
- refresh_from_db() needed to see the real value
- Q(a=1) | Q(b=2)
- filter() kwargs are always AND
- Q wraps a lookup for &, |, ~
- The only way to express OR/NOT
Command
Model.objects.update(count=F("count") + 1)
Model.objects.filter(Q(a=1) | Q(b=2))F("field") defers a computation to the database (race-safe increments, cross-field comparisons) — refresh_from_db() is required to see the real value in Python afterward. Q(...) wraps a lookup so filter() can express OR/NOT, which plain keyword arguments never can.