Django matching query does not exist (tested)

Post matching query does not exist is Post.DoesNotExist, raised when .get() finds no row; get() returned more than one is MultipleObjectsReturned. Catch the model's exception, use .first(), or get_object_or_404 in views.

Setup

One model, an in-memory SQLite table, and three rows: one hello and two dup. common.py:

import sys; sys.path.insert(0, '/w/proj')
import django
from django.conf import settings
settings.configure(INSTALLED_APPS=['blog'],
    DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}})
django.setup()
from django.db import connection
from blog.models import Post
with connection.schema_editor() as ed:
    ed.create_model(Post)
Post.objects.create(title='hello'); Post.objects.create(title='dup'); Post.objects.create(title='dup')
from django.db import models
class Post(models.Model):
    title = models.CharField(max_length=100)

1. Reproduce both errors

from common import *
from django.core.exceptions import ObjectDoesNotExist
for kw in [{'pk': 99}, {'title': 'dup'}]:
    try:
        Post.objects.get(**kw)
    except Exception as e:
        print(kw, '->', type(e).__qualname__ + ':', e)
        print('   ObjectDoesNotExist subclass:', isinstance(e, ObjectDoesNotExist))

Output:

{'pk': 99} -> Post.DoesNotExist: Post matching query does not exist.
   ObjectDoesNotExist subclass: True
{'title': 'dup'} -> Post.MultipleObjectsReturned: get() returned more than one Post -- it returned 2!
   ObjectDoesNotExist subclass: False

.get() expects exactly one row. Zero rows raises <Model>.DoesNotExist ("matching query does not exist"); two or more raises <Model>.MultipleObjectsReturned with the count. Each model has its own subclasses; DoesNotExist also inherits from django.core.exceptions.ObjectDoesNotExist, which is handy for catching the error for any model.

2. Fixes: catch it, or use first()

from common import *
# 1. catch the model-specific exception
try:
    p = Post.objects.get(pk=99)
except Post.DoesNotExist:
    p = None
print('caught:', p)
# 2. first() returns None instead of raising (and never raises MultipleObjectsReturned)
print('first():', Post.objects.filter(pk=99).first(), Post.objects.filter(title='dup').first().pk)
# 3. get_or_create on a non-unique field still raises when duplicates exist
try:
    Post.objects.get_or_create(title='dup')
except Post.MultipleObjectsReturned as e:
    print('get_or_create:', type(e).__qualname__ + ':', e)

Output:

caught: None
first(): None 2
get_or_create: Post.MultipleObjectsReturned: get() returned more than one Post -- it returned 2!

3. In views: get_object_or_404

from common import *
from django.http import Http404
from django.shortcuts import get_object_or_404
for kw in [{'pk': 99}, {'title': 'dup'}]:
    try:
        get_object_or_404(Post, **kw)
    except Exception as e:
        print(kw, '->', type(e).__qualname__ + ':', e)

Output:

{'pk': 99} -> Http404: No Post matches the given query.
{'title': 'dup'} -> Post.MultipleObjectsReturned: get() returned more than one Post -- it returned 2!

get_object_or_404 converts DoesNotExist into Http404 (a 404 page, not a 500), but MultipleObjectsReturned passes through unchanged and becomes a 500 error. Look up by pk or a unique field in views.

Related: Django NoReverseMatch, Django settings are not configured, SQLAlchemy IntegrityError UNIQUE constraint failed.

Frequently asked questions

How do I avoid 'matching query does not exist' in Django?

Catch Model.DoesNotExist, use .filter(...).first() which returns None, or get_object_or_404 in views.

Does get_object_or_404 handle MultipleObjectsReturned?

No. In Django 6.1.2 only DoesNotExist becomes Http404; MultipleObjectsReturned propagates as a 500.

Why does get_or_create raise MultipleObjectsReturned?

It calls get() first, so existing duplicate rows make it raise. Add a unique constraint after removing duplicates.

Written with AI assistance. Measured on 2026-10-23 with Django 6.1.2 and Python 3.14.7 (python:3-alpine in Docker, SQLite in memory). Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python and Django are free.