diff --git a/django/core/exceptions.py b/django/core/exceptions.py index f22f67c261..d9fc326cf2 100644 --- a/django/core/exceptions.py +++ b/django/core/exceptions.py @@ -4,6 +4,10 @@ class ObjectDoesNotExist(Exception): "The requested object does not exist" silent_variable_failure = True +class MultipleObjectsReturned(Exception): + "The query returned multiple objects when only one was expected." + pass + class SuspiciousOperation(Exception): "The user did something suspicious" pass diff --git a/django/db/models/base.py b/django/db/models/base.py index ae951fcaac..31bc907775 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -1,7 +1,7 @@ import django.db.models.manipulators import django.db.models.manager from django.core import validators -from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist from django.db.models.fields.related import OneToOneRel, ManyToOneRel from django.db.models.query import delete_objects @@ -35,6 +35,8 @@ class ModelBase(type): new_class = type.__new__(cls, name, bases, {'__module__': attrs.pop('__module__')}) new_class.add_to_class('_meta', Options(attrs.pop('Meta', None))) new_class.add_to_class('DoesNotExist', types.ClassType('DoesNotExist', (ObjectDoesNotExist,), {})) + new_class.add_to_class('MultipleObjectsReturned', + types.ClassType('MultipleObjectsReturned', (MultipleObjectsReturned, ), {})) # Build complete list of parents for base in bases: diff --git a/django/db/models/query.py b/django/db/models/query.py index 4d0d295e97..0bc36f425a 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -261,7 +261,8 @@ class _QuerySet(object): obj_list = list(clone) if len(obj_list) < 1: raise self.model.DoesNotExist, "%s matching query does not exist." % self.model._meta.object_name - assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs) + elif len(obj_list) > 1: + raise self.model.MultipleObjectsReturned, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs) return obj_list[0] def create(self, **kwargs): diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 611b997736..1cece7c7c0 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -38,7 +38,7 @@ def get_object_or_404(klass, *args, **kwargs): klass may be a Model, Manager, or QuerySet object. All other passed arguments and keyword arguments are used in the get() query. - Note: Like with get(), an AssertionError will be raised if more than one + Note: Like with get(), an MultipleObjectsReturned will be raised if more than one object is found. """ queryset = _get_queryset(klass) diff --git a/docs/shortcuts.txt b/docs/shortcuts.txt index 6c55486b5f..f94babfecb 100644 --- a/docs/shortcuts.txt +++ b/docs/shortcuts.txt @@ -98,8 +98,8 @@ This example is equivalent to:: except MyModel.DoesNotExist: raise Http404 -Note: As with ``get()``, an ``AssertionError`` will be raised if more than -one object is found. +Note: As with ``get()``, an ``MultipleObjectsReturned`` exception will be +raised if more than one object is found. .. _get(): ../db-api/#get-kwargs diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index bd800317d3..d9f276b024 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -78,7 +78,7 @@ Http404: No Article matches the given query. >>> get_object_or_404(Author.objects.all()) Traceback (most recent call last): ... -AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {} +MultipleObjectsReturned: get() returned more than one Author -- it returned ...! Lookup parameters were {} # Using an EmptyQuerySet raises a Http404 error. >>> get_object_or_404(Article.objects.none(), title__contains="Run")