When a Django view needs to fetch a single record by id or slug, Model.objects.get() raises DoesNotExist and returns a 500 error if the record is missing. get_object_or_404() from django.shortcuts does the right thing in one line: if nothing is found, it raises Http404 so Django returns a 404 page. Use it for every detail view that loads an object by id, slug, or another unique key.
The Problem
Imagine a post detail view that loads a post by the slug from the URL. A user mistypes the slug, or the post has been deleted, and Post.objects.get(slug=slug) finds nothing and raises Post.DoesNotExist. If you forget to wrap it in try/except, Django returns a 500 error — ugly, and it signals a server failure instead of simply saying “this page does not exist”.
from django.http import Http404from django.shortcuts import renderdef post_detail(request, slug): try: post = Post.objects.get(slug=slug) except Post.DoesNotExist: raise Http404("Không tìm thấy bài viết.") return render(request, "blog/detail.html", {"post": post})
This works, but every detail view repeats the same try/except block — and a single forgotten wrapper means a 500 error in production.
The Solution
Replace the whole try/except block with a single line:
from django.shortcuts import get_object_or_404, renderdef post_detail(request, slug): post = get_object_or_404(Post, slug=slug) return render(request, "blog/detail.html", {"post": post})
Internally it calls get() on the model and converts DoesNotExist into Http404, so the behavior is identical to the hand-written version with far less code. You can also pass a pre-filtered queryset — for example, only published posts:
from django.shortcuts import get_list_or_404, get_object_or_404def post_detail(request, slug): post = get_object_or_404(Post.objects.filter(status="published"), slug=slug) return render(request, "blog/detail.html", {"post": post})def posts_by_tag(request, tag_slug): posts = get_list_or_404(Post, tags__slug=tag_slug) return render(request, "blog/tag.html", {"posts": posts})
get_list_or_404() is the list counterpart: it filters, returns a list, and raises Http404 when the result is empty. Both functions accept a model, a manager, or a queryset, and they accept Q objects as well as the usual get() and filter() lookups. You drop the repeated code, every view behaves consistently on 404, and a reader sees in one line exactly what happens: “not found means 404”.
Apply It to Your Project
Go through the detail views in your project and replace try/except DoesNotExist blocks with get_object_or_404, starting with views that take parameters from the URL — product details, user profiles, posts. For list pages grouped by category or tag, use get_list_or_404 when an empty category should also count as a 404 rather than a blank page.
Related Posts
- Django: Write Forms Twice as Fast with ModelForm — Stop Mapping Every Field by Hand
- Django: Fetch Only the Records You Need with Conditional Prefetch in 5 Minutes
- Django: Replace NULL with a Default Value Right in the Query with Coalesce in 5 Minutes
FAQ
How is get_object_or_404 different from objects.get()?
objects.get() raises DoesNotExist when no record is found, and Django does not convert that into a 404 on its own, so it easily becomes a 500 error. get_object_or_404() wraps that conversion in, so your view always returns a proper 404 when the record is absent.
Can I pass a pre-filtered queryset to get_object_or_404?
Yes. Passing a pre-filtered queryset is very useful when you want to narrow the search scope — for example, searching only among published posts.
When should I use get_list_or_404 instead of filter()?
Use get_list_or_404 when an empty list is itself an error — a tag page with no posts left, or a category that does not exist. If an empty page is a legitimate state, such as a search with no results, keep filter() and show a “no results” message instead of a 404.
What if several objects match the condition?
get_object_or_404 still raises MultipleObjectsReturned, just like plain get() — it does not turn that into a 404. Always query by a unique field such as id, slug, or a unique key pair, and avoid vague conditions for this function.
Can I use get_object_or_404 in a Django REST Framework API?
Yes, in function views or regular views, because DRF converts Http404 into a 404 response. If you use DRF generic views you do not need to call it by hand: the built-in get_object() method already returns a 404.
💬 Have you tried this in your own project? If you hit an error or found a neater way, leave a comment below — I read them all and will fold the best ideas into the post.
References
- Django Documentation, Shortcut functions — https://docs.djangoproject.com/en/5.2/topics/http/shortcuts/
