summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2017-03-27 18:43:40 +0200
committerGitHub <noreply@github.com>2017-03-27 18:43:40 +0200
commit1b6f05e91f5d1f7aaf937522ead62836ab50fed8 (patch)
tree5a82d39501a9e16609413ff4f11f3e4fdbfbdd45
parent899c42cc8e1820948f4091f815ce7890057c4a81 (diff)
Fixed #21160 -- Fixed QuerySet.in_bulk() crash on SQLite when requesting more than 999 ids.
Thanks Andrei Picus and Anssi Kääriäinen for the initial patch and Tim Graham for the review.
-rw-r--r--django/db/models/query.py12
-rw-r--r--tests/lookup/tests.py11
2 files changed, 22 insertions, 1 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 4e786a239e..8ee1b34117 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -565,7 +565,17 @@ class QuerySet:
if id_list is not None:
if not id_list:
return {}
- qs = self.filter(pk__in=id_list).order_by()
+ batch_size = connections[self.db].features.max_query_params
+ id_list = tuple(id_list)
+ # If the database has a limit on the number of query parameters
+ # (e.g. SQLite), retrieve objects in batches if necessary.
+ if batch_size and batch_size < len(id_list):
+ qs = ()
+ for offset in range(0, len(id_list), batch_size):
+ batch = id_list[offset:offset + batch_size]
+ qs += tuple(self.filter(pk__in=batch).order_by())
+ else:
+ qs = self.filter(pk__in=id_list).order_by()
else:
qs = self._clone()
return {obj._get_pk_val(): obj for obj in qs}
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index f826a480a1..25098af03c 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -1,8 +1,10 @@
import collections
from datetime import datetime
+from math import ceil
from operator import attrgetter
from django.core.exceptions import FieldError
+from django.db import connection
from django.test import TestCase, skipUnlessDBFeature
from .models import Article, Author, Game, Player, Season, Tag
@@ -127,6 +129,15 @@ class LookupTests(TestCase):
with self.assertRaises(TypeError):
Article.objects.in_bulk(headline__startswith='Blah')
+ def test_in_bulk_lots_of_ids(self):
+ test_range = 2000
+ max_query_params = connection.features.max_query_params
+ expected_num_queries = ceil(test_range / max_query_params) if max_query_params else 1
+ Author.objects.bulk_create([Author() for i in range(test_range - Author.objects.count())])
+ authors = {author.pk: author for author in Author.objects.all()}
+ with self.assertNumQueries(expected_num_queries):
+ self.assertEqual(Author.objects.in_bulk(authors.keys()), authors)
+
def test_values(self):
# values() returns a list of dictionaries instead of object instances --
# and you can specify which fields you want to retrieve.