diff --git a/AUTHORS b/AUTHORS index e1a18adf9d..250d10bc8e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -242,6 +242,7 @@ answer newbie questions, and generally made Django that much better: Thomas Steinacher nowell strite Sundance + SuperJared Radek Švarz Swaroop C H Aaron Swartz diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 9d7120bc7a..e4bba0919e 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -7,6 +7,7 @@ for convenience's sake. from django.template import loader from django.http import HttpResponse, Http404 from django.db.models.manager import Manager +from django.db.models.query import QuerySet def render_to_response(*args, **kwargs): """ @@ -16,40 +17,46 @@ def render_to_response(*args, **kwargs): return HttpResponse(loader.render_to_string(*args, **kwargs)) load_and_render = render_to_response # For backwards compatibility. +def _get_queryset(klass): + """ + Return a QuerySet from a Model, Manager, or QuerySet. Created to make + get_object_or_404 and get_list_or_404 more DRY. + """ + if isinstance(klass, QuerySet): + return klass + elif isinstance(klass, Manager): + manager = klass + else: + manager = klass._default_manager + return manager.all() + def get_object_or_404(klass, *args, **kwargs): """ Use get() to return an object, or raise a Http404 exception if the object does not exist. - klass may be a Model or Manager object. All other passed + 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 object is found. """ - if isinstance(klass, Manager): - manager = klass - klass = manager.model - else: - manager = klass._default_manager + queryset = _get_queryset(klass) try: - return manager.get(*args, **kwargs) - except klass.DoesNotExist: - raise Http404('No %s matches the given query.' % klass._meta.object_name) + return queryset.get(*args, **kwargs) + except queryset.model.DoesNotExist: + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) def get_list_or_404(klass, *args, **kwargs): """ Use filter() to return a list of objects, or raise a Http404 exception if - the list is empty. + the list is emtpy. - klass may be a Model or Manager object. All other passed + klass may be a Model, Manager, or QuerySet object. All other passed arguments and keyword arguments are used in the filter() query. """ - if isinstance(klass, Manager): - manager = klass - else: - manager = klass._default_manager - obj_list = list(manager.filter(*args, **kwargs)) + queryset = _get_queryset(klass) + obj_list = list(queryset.filter(*args, **kwargs)) if not obj_list: - raise Http404('No %s matches the given query.' % manager.model._meta.object_name) + raise Http404('No %s matches the given query.' % queryset.model._meta.object_name) return obj_list diff --git a/docs/db-api.txt b/docs/db-api.txt index 8e664ce3c1..9d0d4358f5 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -1891,8 +1891,8 @@ get_object_or_404() One common idiom to use ``get()`` and raise ``Http404`` if the object doesn't exist. This idiom is captured by ``get_object_or_404()``. This function takes a Django model as its first argument and an -arbitrary number of keyword arguments, which it passes to the manager's -``get()`` function. It raises ``Http404`` if the object doesn't +arbitrary number of keyword arguments, which it passes to the default +manager's ``get()`` function. It raises ``Http404`` if the object doesn't exist. For example:: # Get the Entry with a primary key of 3 @@ -1901,7 +1901,7 @@ exist. For example:: When you provide a model to this shortcut function, the default manager is used to execute the underlying ``get()`` query. If you don't want to use the default manager, or if you want to search a list of related objects, -you can provide ``get_object_or_404()`` with a manager object instead. +you can provide ``get_object_or_404()`` with a ``Manager`` object instead. For example:: # Get the author of blog instance e with a name of 'Fred' @@ -1911,6 +1911,14 @@ For example:: # entry with a primary key of 3 e = get_object_or_404(Entry.recent_entries, pk=3) +If you need to use a custom method that you added to a custom manager, +then you can provide ``get_object_or_404()`` with a ``QuerySet`` object. +For example:: + + # Use a QuerySet returned from a 'published' method of a custom manager + # in the search for an entry with primary key of 5 + e = get_object_or_404(Entry.objects.published(), pk=5) + get_list_or_404() ----------------- diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index 5f449f4cfe..573eaf2047 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -3,11 +3,11 @@ get_object_or_404 is a shortcut function to be used in view functions for performing a get() lookup and raising a Http404 exception if a DoesNotExist -exception was rasied during the get() call. +exception was raised during the get() call. get_list_or_404 is a shortcut function to be used in view functions for performing a filter() lookup and raising a Http404 exception if a DoesNotExist -exception was rasied during the filter() call. +exception was raised during the filter() call. """ from django.db import models @@ -69,11 +69,28 @@ Http404: No Article matches the given query. >>> get_object_or_404(Article.by_a_sir, title="Run away!") +# QuerySets can be used too. +>>> get_object_or_404(Article.objects.all(), title__contains="Run") + + +# Just as when using a get() lookup, you will get an error if more than one +# object is returned. +>>> get_object_or_404(Author.objects.all()) +Traceback (most recent call last): +... +AssertionError: 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") +Traceback (most recent call last): +... +Http404: No Article matches the given query. + # get_list_or_404 can be used to get lists of objects >>> get_list_or_404(a.article_set, title__icontains='Run') [] -# Http404 is returned if the list is empty +# Http404 is returned if the list is empty. >>> get_list_or_404(a.article_set, title__icontains='Shrubbery') Traceback (most recent call last): ... @@ -83,4 +100,8 @@ Http404: No Article matches the given query. >>> get_list_or_404(Article.by_a_sir, title__icontains="Run") [] +# QuerySets can be used too. +>>> get_list_or_404(Article.objects.all(), title__icontains="Run") +[] + """}