F() tham chiếu giá trị cột ngay ở DB-side để cập nhật mà không cần đọc về Python — nhờ vậy tránh được race condition khi nhiều request cùng tăng cùng một cột.
from django.db.models import F
# Sai: race nếu 2 request chạy song song
product.views = product.views + 1
product.save()
# Đúng: Django sinh UPDATE ... SET views = views + 1
Product.objects.filter(id=pid).update(views=F('views') + 1)Q() đóng gói điều kiện thành object để combine bằng | (OR), & (AND), ~ (NOT) — .filter(...) mặc định chỉ AND, không gộp OR được.
from django.db.models import Q
Post.objects.filter(
Q(title__icontains='django') | Q(tags__name='django'),
published=True,
)Hai thứ dễ vấp khi dùng: sau khi F() update, instance Python vẫn còn giữ giá trị cũ — muốn đọc giá trị mới phải refresh_from_db().
Còn Q() lồng nhiều OR trên bảng lớn mà không có index thì Postgres planner sẽ chuyển sang seq scan, search chậm như rùa — cân nhắc index GIN/trigram cho search.
F() references a column value on the DB side so an update happens without round-tripping to Python — avoiding race conditions when many requests increment the same column.
from django.db.models import F
# Wrong: race when two requests run in parallel
product.views = product.views + 1
product.save()
# Right: Django generates UPDATE ... SET views = views + 1
Product.objects.filter(id=pid).update(views=F('views') + 1)Q() wraps conditions in an object so you can combine them with | (OR), & (AND), ~ (NOT) — plain .filter(...) is AND-only.
from django.db.models import Q
Post.objects.filter(
Q(title__icontains='django') | Q(tags__name='django'),
published=True,
)Pitfall: After an F() update, the Python instance still holds the old value — call refresh_from_db() if you need the new one.
And many nested OR Q() clauses on a large unindexed table push Postgres to a seq scan; consider GIN/trigram for search.