Django
ORM
database
random record
query

How to pull a random record using Django's ORM?

Master System Design with Codemia

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

Introduction

Fetching a random row sounds easy, but the best Django ORM approach depends on table size and traffic. The shortest query is fine for small datasets, while larger tables usually need a strategy that avoids asking the database to shuffle every row.

The Simple Approach: order_by("?")

For small tables or admin-style tools, the most readable solution is to randomize the queryset and take the first row.

python
1from myapp.models import Article
2
3random_article = Article.objects.order_by("?").first()
4
5if random_article is not None:
6    print(random_article.title)

This works because Django translates the random ordering request into the database's random function. It is correct and compact, which makes it a good default when performance is not critical.

The drawback is cost. Random ordering typically forces the database to assign a random value to many rows and sort them, which becomes expensive as the table grows.

A More Scalable Offset-Based Strategy

If the table is large, one common alternative is:

  1. Count the rows.
  2. Choose a random offset in Python.
  3. Fetch one row at that offset.
python
1import random
2from myapp.models import Article
3
4def get_random_article():
5    total = Article.objects.count()
6    if total == 0:
7        return None
8
9    offset = random.randrange(total)
10    return Article.objects.all()[offset]
11
12article = get_random_article()
13if article:
14    print(article.title)

This avoids random sorting across the full table. The database still needs to skip rows up to the offset, so it is not free, but it often performs better than order_by("?") on large datasets.

Filtering Before Choosing Randomly

In real applications, you rarely want a random row from the entire table. You may need a random published article, a random active product, or a random item in one category. Apply filters before computing the count and offset.

python
1import random
2from myapp.models import Article
3
4def get_random_published_article(category_slug):
5    qs = Article.objects.filter(is_published=True, category__slug=category_slug)
6    total = qs.count()
7    if total == 0:
8        return None
9
10    return qs[random.randrange(total)]

Filtering first keeps the result correct and prevents wasted work on rows that are not eligible anyway.

When Primary Key Sampling Helps

Developers sometimes sample a random primary key value and try to fetch that row. This can be fast if IDs are dense, but it becomes unreliable when rows have been deleted and the ID space has gaps.

python
1import random
2from myapp.models import Article
3
4def get_random_by_id_guess():
5    min_id = Article.objects.order_by("id").values_list("id", flat=True).first()
6    max_id = Article.objects.order_by("-id").values_list("id", flat=True).first()
7
8    if min_id is None or max_id is None:
9        return None
10
11    while True:
12        candidate_id = random.randint(min_id, max_id)
13        article = Article.objects.filter(id=candidate_id).first()
14        if article is not None:
15            return article

This works, but it can loop many times if IDs are sparse. Use it only when you understand the shape of the data.

Common Pitfalls

The biggest mistake is using order_by("?") on a large hot table and expecting it to scale. It may look harmless in development and then become a bottleneck in production.

Another problem is forgetting to handle the empty queryset case. Methods like random.randrange(total) will fail when total is zero, so guard that branch explicitly.

Offset-based selection can also become inconsistent if you build the queryset in multiple steps and the underlying table changes between the count and the fetch. For most applications that is acceptable, but it is worth knowing that the chosen row is not guaranteed to be perfectly stable under heavy concurrent writes.

Finally, avoid converting the whole queryset to a Python list just to call random.choice. That loads every row into memory and defeats the purpose of using the ORM efficiently.

Summary

  • Use order_by("?").first() when the table is small and clarity matters most.
  • Prefer count plus random offset when order_by("?") becomes too expensive.
  • Apply filters before random selection so only eligible rows are considered.
  • Be cautious with primary-key sampling if deleted rows create gaps.
  • Always handle empty querysets and avoid loading the full table into memory.

Course illustration
Course illustration

All Rights Reserved.