summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmir Hadi <hadi.amir@gmail.com>2019-04-13 14:33:05 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-05-12 07:05:24 +0200
commit330638b89f14e1fb06e9d313ccc9768ae167c53f (patch)
treee57e0c7ca508524ae973756d8b1bdeadaba544a1
parent7c3732a1b47bb736ccfd8302aa55e6cf41edff7e (diff)
Fixed #6785 -- Made QuerySet.get() fetch a limited number of rows.
Co-authored-by: Tim Graham <timograham@gmail.com> Co-authored-by: Patryk Zawadzki <patrys@room-303.com> Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
-rw-r--r--django/db/models/query.py13
-rw-r--r--tests/basic/tests.py22
2 files changed, 32 insertions, 3 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 84236107f3..a21a091af0 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -28,6 +28,9 @@ from django.utils import timezone
from django.utils.functional import cached_property, partition
from django.utils.version import get_version
+# The maximum number of results to fetch in a get() query.
+MAX_GET_RESULTS = 21
+
# The maximum number of items to display in a QuerySet.__repr__
REPR_OUTPUT_SIZE = 20
@@ -398,6 +401,10 @@ class QuerySet:
clone = self.filter(*args, **kwargs)
if self.query.can_filter() and not self.query.distinct_fields:
clone = clone.order_by()
+ limit = None
+ if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit:
+ limit = MAX_GET_RESULTS
+ clone.query.set_limits(high=limit)
num = len(clone)
if num == 1:
return clone._result_cache[0]
@@ -407,8 +414,10 @@ class QuerySet:
self.model._meta.object_name
)
raise self.model.MultipleObjectsReturned(
- "get() returned more than one %s -- it returned %s!" %
- (self.model._meta.object_name, num)
+ 'get() returned more than one %s -- it returned %s!' % (
+ self.model._meta.object_name,
+ num if not limit or num < limit else 'more than %s' % (limit - 1),
+ )
)
def create(self, **kwargs):
diff --git a/tests/basic/tests.py b/tests/basic/tests.py
index 328ddc31f1..b29dda64f7 100644
--- a/tests/basic/tests.py
+++ b/tests/basic/tests.py
@@ -4,7 +4,7 @@ from datetime import datetime, timedelta
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.db import DEFAULT_DB_ALIAS, DatabaseError, connections, models
from django.db.models.manager import BaseManager
-from django.db.models.query import EmptyQuerySet, QuerySet
+from django.db.models.query import MAX_GET_RESULTS, EmptyQuerySet, QuerySet
from django.test import (
SimpleTestCase, TestCase, TransactionTestCase, skipUnlessDBFeature,
)
@@ -387,6 +387,26 @@ class ModelTest(TestCase):
# Fields that weren't deleted aren't reloaded.
self.assertEqual(article.pub_date, new_pub_date)
+ def test_multiple_objects_max_num_fetched(self):
+ max_results = MAX_GET_RESULTS - 1
+ Article.objects.bulk_create(
+ Article(headline='Area %s' % i, pub_date=datetime(2005, 7, 28))
+ for i in range(max_results)
+ )
+ self.assertRaisesMessage(
+ MultipleObjectsReturned,
+ 'get() returned more than one Article -- it returned %d!' % max_results,
+ Article.objects.get,
+ headline__startswith='Area',
+ )
+ Article.objects.create(headline='Area %s' % max_results, pub_date=datetime(2005, 7, 28))
+ self.assertRaisesMessage(
+ MultipleObjectsReturned,
+ 'get() returned more than one Article -- it returned more than %d!' % max_results,
+ Article.objects.get,
+ headline__startswith='Area',
+ )
+
class ModelLookupTest(TestCase):
@classmethod