[1.5.X] Fixed #15361 - Documented performance considerations for QuerySet.get()

Thanks mmcnickle for the patch.

Backport of feaf9f279a from master
This commit is contained in:
Tim Graham 2012-11-02 06:54:00 -04:00
parent 6b1f73113a
commit ffc649df88
2 changed files with 70 additions and 1 deletions

View File

@ -132,6 +132,41 @@ Write your own :doc:`custom SQL to retrieve data or populate models
</topics/db/sql>`. Use ``django.db.connection.queries`` to find out what Django
is writing for you and start from there.
Retrieve individual objects using a unique, indexed column
==========================================================
There are two reasons to use a column with
:attr:`~django.db.models.Field.unique` or
:attr:`~django.db.models.Field.db_index` when using
:meth:`~django.db.models.query.QuerySet.get` to retrieve individual objects.
First, the query will be quicker because of the underlying database index.
Also, the query could run much slower if multiple objects match the lookup;
having a unique constraint on the column guarantees this will never happen.
So using the :ref:`example Weblog models <queryset-model-example>`::
>>> entry = Entry.objects.get(id=10)
will be quicker than:
>>> entry = Entry.object.get(headline="News Item Title")
because ``id`` is indexed by the database and is guaranteed to be unique.
Doing the following is potentially quite slow:
>>> entry = Entry.objects.get(headline__startswith="News")
First of all, `headline` is not indexed, which will make the underlying
database fetch slower.
Second, the lookup doesn't guarantee that only one object will be returned.
If the query matches more than one object, it will retrieve and transfer all of
them from the database. This penalty could be substantial if hundreds or
thousands of records are returned. The penalty will be compounded if the
database lives on a separate server, where network overhead and latency also
play a factor.
Retrieve everything at once if you know you will need it
========================================================

View File

@ -2,7 +2,7 @@ from __future__ import absolute_import, unicode_literals
from datetime import datetime
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db.models.fields import Field, FieldDoesNotExist
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import six
@ -128,6 +128,40 @@ class ModelTest(TestCase):
b = Article.objects.get(pk=a.id)
self.assertEqual(a, b)
# Create a very similar object
a = Article(
id=None,
headline='Area man programs in Python',
pub_date=datetime(2005, 7, 28),
)
a.save()
self.assertEqual(Article.objects.count(), 2)
# Django raises an Article.MultipleObjectsReturned exception if the
# lookup matches more than one object
self.assertRaisesRegexp(
MultipleObjectsReturned,
"get\(\) returned more than one Article -- it returned 2!",
Article.objects.get,
headline__startswith='Area',
)
self.assertRaisesRegexp(
MultipleObjectsReturned,
"get\(\) returned more than one Article -- it returned 2!",
Article.objects.get,
pub_date__year=2005,
)
self.assertRaisesRegexp(
MultipleObjectsReturned,
"get\(\) returned more than one Article -- it returned 2!",
Article.objects.get,
pub_date__year=2005,
pub_date__month=7,
)
def test_object_creation(self):
# Create an Article.
a = Article(