Fixed #15361 - Documented performance considerations for QuerySet.get()

Thanks mmcnickle for the patch.
This commit is contained in:
Tim Graham 2012-11-02 06:54:00 -04:00
parent 92fc263a28
commit feaf9f279a
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 </topics/db/sql>`. Use ``django.db.connection.queries`` to find out what Django
is writing for you and start from there. 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 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 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.db.models.fields import Field, FieldDoesNotExist
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import six from django.utils import six
@ -128,6 +128,40 @@ class ModelTest(TestCase):
b = Article.objects.get(pk=a.id) b = Article.objects.get(pk=a.id)
self.assertEqual(a, b) 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): def test_object_creation(self):
# Create an Article. # Create an Article.
a = Article( a = Article(