Django
database
query
ORM
filtering

Get the latest record with filter in Django

Master System Design with Codemia

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

Introduction

To get the latest record that matches a filter in Django, you usually combine filter() with an ordering field such as created_at, updated_at, or an auto-incrementing primary key. The main decision is whether you want an exception when no record matches or a quiet None. Django supports both patterns, and choosing the right one keeps the query behavior explicit.

Use filter(...).order_by(...).first()

The most common pattern is to sort the filtered rows in descending order and take the first result. This is readable and returns None when no row matches.

python
1from django.db import models
2
3
4class Invoice(models.Model):
5    customer_id = models.IntegerField()
6    status = models.CharField(max_length=20)
7    created_at = models.DateTimeField(auto_now_add=True)
8
9
10latest_invoice = (
11    Invoice.objects
12    .filter(customer_id=42, status="paid")
13    .order_by("-created_at")
14    .first()
15)
16
17print(latest_invoice)

This is usually the best default because it handles the empty case safely without raising an exception.

Use latest() When No Match Should Be Exceptional

Django also provides latest(), which returns one object ordered by the given field. Unlike first(), it raises DoesNotExist if the queryset is empty.

python
1try:
2    latest_invoice = (
3        Invoice.objects
4        .filter(customer_id=42, status="paid")
5        .latest("created_at")
6    )
7except Invoice.DoesNotExist:
8    latest_invoice = None

This is a good choice when the absence of a record is abnormal and should be handled explicitly.

Define a Default Latest Field in the Model

If the same model is often queried for its latest row, you can define get_latest_by in Meta. That allows latest() to be called without repeating the field name everywhere.

python
1from django.db import models
2
3
4class Invoice(models.Model):
5    customer_id = models.IntegerField()
6    status = models.CharField(max_length=20)
7    created_at = models.DateTimeField(auto_now_add=True)
8
9    class Meta:
10        get_latest_by = "created_at"

Then the query becomes:

python
1latest_invoice = (
2    Invoice.objects
3    .filter(customer_id=42, status="paid")
4    .latest()
5)

This keeps the code concise, but it only makes sense if one field is the clear definition of "latest" across the model.

Pick the Right Ordering Field

The query is only as correct as the field you sort by. If "latest" means most recently created, use a creation timestamp. If it means most recently updated, use an update timestamp. If the primary key is monotonically increasing and creation order is sufficient, ordering by -id can also work.

python
1latest_by_id = (
2    Invoice.objects
3    .filter(customer_id=42, status="paid")
4    .order_by("-id")
5    .first()
6)

This can be fine for simple tables, but timestamps are usually clearer and more portable as business logic.

Make the Query Efficient

Filtering and ordering can be expensive on large tables if the relevant fields are not indexed. If the query is common, consider database indexes on the filtering columns and the ordering column.

For example, if the app frequently asks for the latest paid invoice by customer, an index strategy should support both customer_id, status, and the sort field. The exact best index depends on the database, but the principle is stable: frequent filtered-ordering queries deserve indexing attention.

Beware of Ambiguous Ties

Two rows can share the same timestamp. If that matters, add a second ordering key so the result is deterministic.

python
1latest_invoice = (
2    Invoice.objects
3    .filter(customer_id=42, status="paid")
4    .order_by("-created_at", "-id")
5    .first()
6)

This avoids edge cases where two records have the same created_at down to the stored precision.

Common Pitfalls

  • Calling latest() without handling the possibility that no records match.
  • Ordering by the wrong field and getting the latest updated record when the business rule meant latest created record.
  • Assuming id always represents business recency in systems with data imports or migrations.
  • Ignoring tie-breaking when timestamps can repeat.
  • Forgetting database indexes on fields used repeatedly for filtering and ordering.

Summary

  • The safest default is usually filter(...).order_by("-field").first().
  • Use latest("field") when an empty result should raise an exception.
  • Define get_latest_by when one field consistently defines recency for the model.
  • Choose the ordering field based on business meaning, not convenience alone.
  • Add indexes and deterministic tie-breakers for production queries.

Course illustration
Course illustration

All Rights Reserved.