Sitemap

Django: Top 40 Useful QuerySets

7 min readApr 3, 2025

--

https://miro.medium.com/v2/resize:fit:641/1*IZVNhiXeXbCnBuAvyZ267Q.jpeg

✨ Django’s ORM is super powerful, and the real magic lies in its QuerySet methods. Whether you’re building a simple blog or a complex e-commerce system, knowing how to use these QuerySets effectively can save you a ton of time.

Here are 40 Django QuerySet methods that I find incredibly useful — explained simply and with examples.

# 🔹 Top 40 Useful Django QuerySet Methods

# 📦 Basic & Everyday Use
1. all()
2. filter()
3. exclude()
4. get()
5. order_by()
6. values()
7. values_list()
8. annotate()
9. aggregate()
10. distinct()
11. exists()
12. select_related()
13. prefetch_related()
14. update()
15. delete()
16. create()
17. bulk_create()
18. in_bulk()
19. select_for_update()
20. defer() / only()

# 🛠 Query Logic & Filtering
21. reverse()
22. earliest() / latest()
23. first() / last()
24. count()
25. get_or_create()
26. update_or_create()
27. raw()
28. iterator()
29. union() / intersection() / difference()
30. db_manager()

# 🔍 Advanced & Custom Logic
31. Q()
32. F()
33. Case / When
34. from_queryset() # สร้าง custom manager
35. Subquery() / OuterRef()
36. Exists()
37. as_manager()
38. select_for_update(nowait=True) / skip_locked=True
39. select_for_update(of=...)
40. none()

Extra notes:

If you want to cut query times like a Genius please considerate in using.

  1. select_related()
    Reduce N+1 queries
    For related object (foreign key, one-to-one).
  2. prefetch_related()
    For Many-to-Many and Reverse Foreign key.
  3. annotate()
    Use query in SQL level like Count().
  4. only() เอา, defer() ไม่เอา
    To reduce all fields in model from default.
    Smaller queries > less memory > faster network > better DB caching.
  5. exists()
    When just need to check have or have no.

And finally remember to use tool: django-debug-toolbar to check result.

1. all()

Returns all objects in the table.

Product.objects.all()  # อันนี้ใช่บ่อยจนไม่น่ามาเขียนล่ะนะ 55

2. filter()

Filter objects based on conditions.

Product.objects.filter(price__gt=100)

3. exclude()

Exclude specific objects from the result.

Product.objects.exclude(category="Clothing")

4. get()

Get exactly one object. Raises error if none or more than one found.

Product.objects.get(id=1)  # อันนี้ต้องระวังนะ ถ้า get ไม่เจอจะพังได้ ได้ object กลับมาเลย

5. order_by()

Sort the results.

Product.objects.order_by('-price')  # Descending

6. values()

Returns dictionaries instead of model instances.

# ได้กลับมาเป็น list<dict> นะ เฉพาะ field ที่เราต้องการเท่านั้น

Product.objects.values('name', 'price') # <QuerySet [{'name': 'Q', 'price': '500'}]>

7. values_list()

Like values() but returns tuples.

Product.objects.values_list('name', flat=True)  # <QuerySet ['Q', 'Ji']>

# ถ้้าอยากได้ list ก็เอา list() ครอบได้เลยนะ

8. annotate()

Add calculated fields.

from django.db.models import Count

Category.objects.annotate(product_count=Count('product'))

# สร้าง field ชื่อ "product_count" เพื่อใช้ในการ Count() นะ
# การทำแบบนี้จะเป็นการใช้ SQL level ในการ count ช่วยลดการ hit db or N+1
# ถ้าเทียบกับการ count แบบนี้จะช้ามากๆ
  • Used case we can use to reduce query time spending.
# Basic way to count objects.
# Fetch all related objects and use len() which is take times.
len(author.books.all())


# Better way.
# Count() will fetech at SQL level (no fetching full objects, just to count them).
# Count() no fetching full objects just to count them.
from django.db.models import Count

authors = Author.objects.annotate(book_count=Count('books'))
for author in authors:
print(author.book_count)

9. aggregate()

Return summary values (not per object).

from django.db.models import Sum

Product.objects.aggregate(Sum('price'))

# จะได้ Summary ของ all fields "price" นะ

10. distinct()

Remove duplicates.

Product.objects.values('category').distinct()

11. exists()

Check if any record exists.

Product.objects.filter(price__lt=100).exists()  # มียาง ตัวเทอ
  • ถ้าแค่ต้องการเช็คว่ามีหรือไม่มี เราจะไม่ใช้แบบนี้นะ
# Inefficient way
if Product.objects.filter(price__lt=100).first():
# do something.


# Efficient way because will not fetch object itself.
# It will generate only 1 query instead of pulling full rows.
if Product.objects.filter(price__lt=100).exists():
# do something.

12. select_related()

Optimize DB query by joining related object (foreign key, one-to-one).

Order.objects.select_related('customer')

Another example if we don’t use select_relate() will cause issue N+1 like:

# The problem when not use select_related()
# 1 query for authors
authors = Author.objects.all()

# N queries for books
for author in authors:
books = author.books.all() # extra query every time



# The solution:
# It will help to join table in SQL level.
authors = Author.objects.select_related('profile').all()

13. prefetch_related()

Optimize DB query for many-to-many or reverse foreign key.

Product.objects.prefetch_related('tags')

14. update()

Bulk update.

Product.objects.filter(price__lt=100).update(price=100)

# เป็นการ update "price=100" ให้กับทุก rows ที่ได้จาก filter ด้านหน้านะ

15. delete()

Delete objects.

Product.objects.filter(is_expired=True).delete()

16. create()

Create and save an object.

Product.objects.create(name="Hat", price=250)

17. bulk_create()

Create multiple objects at once.

Product.objects.bulk_create([
Product(name="Pen", price=30),
Product(name="Notebook", price=100),
])

18. in_bulk()

Returns a dictionary of objects keyed by their IDs.

  • เป็นการดึงข้อมูลหลายรายการในครั้งเดียวนะ และ return as dictionary.
  • ใน in_bulk() จะรับแค่ list of id เท่านั้นนะ by default.
  • ถ้า id ที่ใส่ไปไม่มีใน db ก็ไม่พังนะ แค่ไม่พ่น reponse นั้นๆกลับมา
User.objects.in_bulk([1, 2, 3])

# {2: <User: system>, 3: <User: support>, 1: <User: admin>}
# key = id, value = object นั้นๆ

19. select_for_update() ✅ : Row-Level Locking

Locks rows until the end of transaction — useful in concurrent-safe updates like stock or balance.

Get Grassroot Engineer’s stories in your inbox

Join Medium for free to get updates from this writer.

พูดง่ายๆมันจะล๊อคเฉพาะแถวข้อมูล (row) ที่ตรงกับเงื่อนไขใน query โดยที่ไม่ได้ล็อกทั้งตารางนะ

  • เป็นการ lock row in db ไว้ เพื่อป้องกันการแก้ไขข้อมูลซ้ำซ้อนในกรณีที่มีหลาย transactions เข้าถึงข้อมูลพร้อมกัน (concurrent access) จนกว่าจะจบ transaction.atomic()
  • เพื่อป้องกัน race condition.
  • ใช้ใน business logic ที่ต้องการความแม่นยำ เช่น การตัด stock, การโอนเงิน
  • ต้องใช้ร่วมกับ transaction.atomic() เท่านั้นนะ
  • nowait=True คือ ถ้า row นั้นถูก lock ไว้ก่อนแล้ว ให้ไม่ต้องรอ + โยน error เลย โดย default มันคือ nowait=False (รอ…จนได้ล๊อก หรือ รอจนกว่า row นั้นจะ unlock นั่นแหละ)
from django.db import transaction

with transaction.atomic():
product = Product.objects.select_for_update(nowait=True).get(id=1)
product.stock -= 1
product.save()

💡 Use case: Prevent two users from buying the last piece of a product at the same time.

20. defer() / only()

Load only specific fields (useful for performance).

  • ใช้เพื่อลดจำนวน field ที่ดึงมาจาก db
  • ต่างกันที่ defer = ไม่เอา, only = เอา
  • defer(‘description’, ‘image’) > ไม่โหลด description กับ image
  • only(‘name’, ‘price’) > โหลดเฉพาะ name และ price เท่านั้น
  • เพราะ default มันจะ fetches all fields แม้ว่าเราจะไม่ใช้มันก็ตาม ฉะนั้นถ้าอันไหนข้อมูลเยอะ แล้วเราไม่ใช้มันแน่ๆ ก็ไม่ต้องเอามาจะดีกว่านะ
Product.objects.only('name')  # Will fetch only 'name' from DB

21. reverse()

Reverses the order of a QuerySet. Must follow an order_by().

  • แค่เป็นการ reverse() ตัว order_by() เท่านั้นนะ
Product.objects.order_by('price').reverse()

22. earliest() / latest()

Returns the object with the earliest or latest date based on a field.

  • ได้เป็น object กลับมาเลยนะ
Product.objects.earliest('created_at')  # หาข้อมูลที่เก่าสุด เช่น อันแรกสุด
Product.objects.latest('created_at') # หาข้อมูลล่าสุด (ตรงข้ามกับอันบนนะ)

23. first() / last()

Returns the first or last object in a QuerySet.

  • ได้เป็น object กลับมา ซึ่งคือตัวแรกหรือตัวสุดท้ายจาก queryset
Product.objects.filter(category="Clothing").first()

24. count()

Returns the total number of objects in a QuerySet.

Product.objects.count()

25. get_or_create()

Tries to get an object, and creates it if it doesn’t exist.

  • get ค่ามาก่อน ถ้าไม่มีค่อย create จาก defaults
product, created = Product.objects.get_or_create(name="Bag", defaults={'price': 300})

26. update_or_create()

Updates the object if it exists; otherwise, creates it.

  • update ก่อนถ้ามี แต่ถ้าไม่เจอค่อย create จาก defaults
Product.objects.update_or_create(name="Pen", defaults={'price': 35})

27. raw()

Executes raw SQL queries.

  • สำหรับเขียน SQL เองแทนการใช้ ORM
  • เราจะใช้ในกรณีที่ต้องการ optimize เป็นพิเศษ
  • แต่ไม่ควรใช้บ่อยนะ เพราะเสี่ยงจาก SQL injection.
Product.objects.raw('SELECT * FROM myapp_product WHERE price > %s', [100])

# มันคือ SELECT * FROM myapp_product WHERE price > 100

28. iterator()

Yields results one at a time to save memory — great for big datasets.

  • ใช้สำหรับ load ข้อมูลแบบ batch (default = 2000)
  • ใช้สำหรับ large data เช่น generate CSV
  • เพื่อลด memory usage ของ server เพราะถ้าใช้ .all() กับ large data จะกินแรมเยอะ
  • พูดให้ง่ายที่สุด ถ้าใช้ User.objects.all(), Django จะโหลด data ทั้งหมดมาเก็บไว้ใน RAM นะ อาจทำให้ server เราเดี้ยงได้เลย ฉะนั้นถ้า data เยอะ ก็ไปใช้ iterator()ให้ทยอยๆ ทำงานจ้า
for product in Product.objects.iterator(chunk_size=1000):
print(product.name)

29. union(), intersection(), difference()

Set operations between QuerySets.

  • union() = เป็นการ combine 2 QuerySet เข้าด้วยกัน (ต้องไม่ซ้ำกันนะ)
q1 = Product.objects.filter(category="Clothing")
q2 = Product.objects.filter(category="Accessories")

combined = q1.union(q2)

# 👉 combined จะได้สินค้าในทั้งหมวด "Clothing" และ "Accessories" รวมกันไม่มีซ้ำกัน
  • intersection() = หา result ที่อยู่ในทั้ง 2 QuerySet
q1 = Product.objects.filter(price__gt=100)
q2 = Product.objects.filter(stock__gt=0)

common = q1.intersection(q2)

# 👉 common จะได้สินค้าที่ มีราคา > 100 และ stock > 0
  • defference() = หาค่าที่อยู่ในชุดแรก แต่ไม่อยู่ในชุดหลัง
q1 = Product.objects.filter(category="Clothing")
q2 = Product.objects.filter(price__lt=300)

result = q1.difference(q2)

# 👉 result คือสินค้าที่เป็น "Clothing" แต่ ราคาไม่ต่ำกว่า 300

30. db_manager()

Specify which database to use in a multi-database setup.

DATABASES = {
'default': {...},
'replica': {...}
}

# เช่น เห็นไหมว่ามี 2 database
Product.objects.db_manager('replica').all()

# 'replica' คือ ชื่อ database ใช้ในกรณีที่ project เรามีหลาย db นั่นเอง

31. Q()

Build complex queries using AND, OR, and NOT logic.

from django.db.models import Q

Product.objects.filter(Q(price__lt=100) | Q(category="Accessories"))

32. F()

Reference model fields directly in queries (great for comparisons or updates).

  • F() ใช้เพื่อ อ้างถึงค่าใน field ของ model เดียวกัน ใน query ที่ Django สร้าง
  • อัปเดตค่าด้วย field เอง
  • เปรียบเทียบค่าระหว่าง field
  • ลดรอบการ query (efficient มาก)
from django.db.models import F

Product.objects.update(stock=F('stock') - 1)

# นี่จะเป็นการลด stock โดยไม่ต้องดึงค่ามาก่อน
# UPDATE product SET stock = stock - 1 WHERE id = 1;

33. Case / When

Add conditional logic inside annotations.

  • เป็น Query Expression ที่ทรงพลังอีกตัวนะ สำหรับใส่เงื่อนไขแบบ if-else เลย
  • เป็นการเขียน เงื่อนไขแบบ if-elif-else ภายใน .annotate() หรือ .update() เพื่อใส่ค่าใหม่ลงใน field ที่คำนวณ
from django.db.models import Case, When, Value, CharField

Product.objects.annotate(
status=Case(
When(stock=0, then=Value("Out of stock")),
default=Value("In stock"),
output_field=CharField(),
)
)

# 👉 สิ่งที่ได้คือแต่ละ Product จะมี field status ที่เป็น "In stock" หรือ "Out of stock"
# output_field เพื่อกำหนดให้ตรง type เท่านั้นนะ
Order.objects.annotate(
label=Case(
When(total__gt=1000, then=Value("VIP")),
When(total__gt=500, then=Value("Regular")),
default=Value("Newbie"),
output_field=CharField()
)
)
Product.objects.update(
label=Case(
When(stock=0, then=Value("Out")),
default=Value("Available")
)
)

# ใช้ใน .update() ก็ได้

34. from_queryset()

Define a custom manager using a custom QuerySet.

  • from_queryset() เป็น วิธีสร้าง custom manager โดยเอา method จาก custom QuerySet มาใส่ให้เลยอัตโนมัติ — ไม่ต้องเขียน method ซ้ำใน Manager อีกครั้ง

✅ ปกติแล้วเขียนแบบนี้ (ยาวหน่อย)

class ProductQuerySet(models.QuerySet):
def cheap(self):
return self.filter(price__lt=100)

class ProductManager(models.Manager):
def cheap(self):
return self.get_queryset().cheap()class ProductQuerySet(models.Q

🎉 ถ้าใช้ from_queryset() จะเขียนสั้นลงแบบนี้:

class ProductQuerySet(models.QuerySet):
def cheap(self):
return self.filter(price__lt=100)

class ProductManager(models.Manager.from_queryset(ProductQuerySet)):
pass

class Product(models.Model):
...
objects = ProductManager()

✅ ใช้งาน:

Product.objects.cheap()

# 👉 เรียก method cheap() ได้เลยผ่าน .objects โดยไม่ต้อง duplicate logic ใน Manager
# เห็นไหม .cheap() เป็น manager ที่เราสร้างขึ้นมาเองนะ

📌 ประโยชน์ของ from_queryset():

  • DRY (Don’t Repeat Yourself) — ไม่ต้องเขียนซ้ำ
  • ทำให้ method ทั้งหมดใน QuerySet กลายเป็น method ของ Manager อัตโนมัติ
  • ช่วยให้แยก logic ออกมาแบบ OOP มากขึ้น

🧠 ใช้เมื่อไหร่?

  • เมื่อคุณมีหลาย query logic แบบ reusable (เช่น .available(), .published(), .vip_only())
  • เมื่อคุณอยากทำ queryset chainable เช่น Product.objects.available().filter(...)

35. Subquery() and OuterRef()

Use values from one query in another — great for advanced filtering or annotations.

from django.db.models import Subquery, OuterRef

latest_order = Order.objects.filter(user=OuterRef('pk')).order_by('-created_at')
User.objects.annotate(last_order_date=Subquery(latest_order.values('created_at')[:1]))

36. Exists()

Use to test if a related subquery exists (like .filter().exists() but at DB level).

from django.db.models import Exists, OuterRef

orders = Order.objects.filter(user=OuterRef('pk'))
User.objects.annotate(has_orders=Exists(orders))

37. as_manager()

Convert a custom QuerySet into a manager.

class ProductQuerySet(models.QuerySet):
def available(self):
return self.filter(is_available=True)

Product.objects = ProductQuerySet.as_manager()

38. select_for_update(nowait=True) / skip_locked=True

Add locking behavior without waiting or skipping locked rows.

  • nowait=True → ไม่รอ ถ้าล็อกอยู่ → โยน error ทันที
  • skip_locked=True → ข้ามแถวที่ถูกล็อกไปเลย (ดีสำหรับ worker queue)
Product.objects.select_for_update(nowait=True, skip_locked=True)

39. select_for_update(of=...) (PostgreSQL only)

Advanced Postgres feature to specify which tables to lock.

Product.objects.select_for_update(of=('self',))

40. none()

Returns an empty QuerySet — useful as a default value.

Product.objects.none()

Final Thoughts

These QuerySet methods are like tools in your toolbox. You don’t need all of them all the time — but when you do, knowing how to use them can make your Django code cleaner, faster, and more reliable.

If you’re just starting out, pick a few and try using them in your next project. Trust me, Django’s ORM is addictive once you get the hang of it 😄

References:
- https://docs.djangoproject.com/en/5.1/ref/models/querysets/
- Chat เพิ่ลรัก

If you think it’s useful for you, just clap your hands 👏 to be encouraged me.

GRASSROOT ENGINEER 😘

--

--

Grassroot Engineer
Grassroot Engineer

Written by Grassroot Engineer

From ATM engineer to Full Stack Developer who is interested in CODING and believe in EFFORT. — https://grassrootengineer.com