From e1c6e987d07efb3ee9471e63541f9561b461ea44 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 3 Jan 2007 14:16:58 +0000 Subject: [PATCH] Fixed #2756 -- Modified the get_object_or_404/get_list_or_404 shortcuts to accept model managers as well as model classes. Thanks, Gary Wilson. git-svn-id: http://code.djangoproject.com/svn/django/trunk@4275 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/shortcuts/__init__.py | 15 +++- docs/db-api.txt | 40 +++++++++ docs/tutorial03.txt | 2 +- .../modeltests/get_object_or_404/__init__.py | 0 tests/modeltests/get_object_or_404/models.py | 86 +++++++++++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 tests/modeltests/get_object_or_404/__init__.py create mode 100644 tests/modeltests/get_object_or_404/models.py diff --git a/django/shortcuts/__init__.py b/django/shortcuts/__init__.py index 76d54917ad..be2155bb09 100644 --- a/django/shortcuts/__init__.py +++ b/django/shortcuts/__init__.py @@ -4,20 +4,29 @@ from django.template import loader from django.http import HttpResponse, Http404 - +from django.db.models.manager import Manager 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_object_or_404(klass, *args, **kwargs): + if isinstance(klass, Manager): + manager = klass + klass = manager.model + else: + manager = klass._default_manager try: - return klass._default_manager.get(*args, **kwargs) + return manager.get(*args, **kwargs) except klass.DoesNotExist: raise Http404 def get_list_or_404(klass, *args, **kwargs): - obj_list = list(klass._default_manager.filter(*args, **kwargs)) + if isinstance(klass, Manager): + manager = klass + else: + manager = klass._default_manager + obj_list = list(manager.filter(*args, **kwargs)) if not obj_list: raise Http404 return obj_list diff --git a/docs/db-api.txt b/docs/db-api.txt index 13a32bd0b8..0af2c773fb 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -1704,6 +1704,46 @@ For every ``ImageField``, the object will have ``get_FOO_height()`` and ``get_FOO_width()`` methods, where ``FOO`` is the name of the field. This returns the height (or width) of the image, as an integer, in pixels. +Shortcuts +========= + +As you develop views, you will discover a number of common idioms in the +way you use the database API. Django encodes some of these idioms as +shortcuts that can be used to simplify the process of writing views. + +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 +exist. For example:: + + # Get the Entry with a primary key of 3 + e = get_object_or_404(Entry, pk=3) + +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 you want to search a list of related objects, +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' + a = get_object_or_404(e.authors, name='Fred') + + # Use a custom manager 'recent_entries' in the search for an + # entry with a primary key of 3 + e = get_object_or_404(Entry.recent_entries, pk=3) + +get_list_or_404() +----------------- + +``get_list_or_404`` behaves the same was as ``get_object_or_404()`` +-- except the it uses using ``filter()`` instead of ``get()``. It raises +``Http404`` if the list is empty. + Falling back to raw SQL ======================= diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index c4c1b4c546..41d11d9e6e 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -300,7 +300,7 @@ rewritten:: The ``get_object_or_404()`` function takes a Django model module as its first argument and an arbitrary number of keyword arguments, which it passes to the -module's ``get_object()`` function. It raises ``Http404`` if the object doesn't +module's ``get()`` function. It raises ``Http404`` if the object doesn't exist. .. admonition:: Philosophy diff --git a/tests/modeltests/get_object_or_404/__init__.py b/tests/modeltests/get_object_or_404/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py new file mode 100644 index 0000000000..79bcc6f52c --- /dev/null +++ b/tests/modeltests/get_object_or_404/models.py @@ -0,0 +1,86 @@ +""" +34. DB-API Shortcuts + +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. + +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. +""" + +from django.db import models +from django.http import Http404 +from django.shortcuts import get_object_or_404, get_list_or_404 + +class Author(models.Model): + name = models.CharField(maxlength=50) + + def __str__(self): + return self.name + +class ArticleManager(models.Manager): + def get_query_set(self): + return super(ArticleManager, self).get_query_set().filter(authors__name__icontains='sir') + +class Article(models.Model): + authors = models.ManyToManyField(Author) + title = models.CharField(maxlength=50) + objects = models.Manager() + by_a_sir = ArticleManager() + + def __str__(self): + return self.title + +__test__ = {'API_TESTS':""" +# Create some Authors. +>>> a = Author.objects.create(name="Brave Sir Robin") +>>> a.save() +>>> a2 = Author.objects.create(name="Patsy") +>>> a2.save() + +# No Articles yet, so we should get a Http404 error. +>>> get_object_or_404(Article, title="Foo") +Traceback (most recent call last): +... +Http404 + +# Create an Article. +>>> article = Article.objects.create(title="Run away!") +>>> article.authors = [a, a2] +>>> article.save() + +# get_object_or_404 can be passed a Model to query. +>>> get_object_or_404(Article, title__contains="Run") + + +# We can also use the the Article manager through an Author object. +>>> get_object_or_404(a.article_set, title__contains="Run") + + +# No articles containing "Camelot". This should raise a Http404 error. +>>> get_object_or_404(a.article_set, title__contains="Camelot") +Traceback (most recent call last): +... +Http404 + +# Custom managers can be used too. +>>> get_object_or_404(Article.by_a_sir, title="Run away!") + + +# 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 +>>> get_list_or_404(a.article_set, title__icontains='Shrubbery') +Traceback (most recent call last): +... +Http404 + +# Custom managers can be used too. +>>> get_list_or_404(Article.by_a_sir, title__icontains="Run") +[] + +"""}