summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaustubh <55352418+kc611@users.noreply.github.com>2020-08-22 11:23:50 +0530
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-08-26 09:43:39 +0200
commitb9be11d44265308863e4e8cfb458cd3605091452 (patch)
treecf3ad9584a62bcd031c83b333fdd6bba0905b266
parent547a07fa7ec4364ea9ecd2aabfdd16ee4c63003c (diff)
Fixed #31918 -- Allowed QuerySet.in_bulk() to fetch on a single distinct field.
-rw-r--r--django/db/models/query.py3
-rw-r--r--docs/ref/models/querysets.txt11
-rw-r--r--docs/releases/3.2.txt4
-rw-r--r--tests/lookup/tests.py18
4 files changed, 33 insertions, 3 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index bce48e17ef..b39a988ab0 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -699,7 +699,8 @@ class QuerySet:
if (
field_name != 'pk' and
not opts.get_field(field_name).unique and
- field_name not in unique_fields
+ field_name not in unique_fields and
+ not self.query.distinct_fields == (field_name,)
):
raise ValueError("in_bulk()'s field_name must be a unique field but %r isn't." % field_name)
if id_list is not None:
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index e5c0038528..228e2cf736 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2250,8 +2250,9 @@ database query like ``count()`` would.
Takes a list of field values (``id_list``) and the ``field_name`` for those
values, and returns a dictionary mapping each value to an instance of the
object with the given field value. If ``id_list`` isn't provided, all objects
-in the queryset are returned. ``field_name`` must be a unique field, and it
-defaults to the primary key.
+in the queryset are returned. ``field_name`` must be a unique field or a
+distinct field (if there's only one field specified in :meth:`distinct`).
+``field_name`` defaults to the primary key.
Example::
@@ -2265,9 +2266,15 @@ Example::
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
+ >>> Blog.objects.distinct('name').in_bulk(field_name='name')
+ {'Beatles Blog': <Blog: Beatles Blog>, 'Cheddar Talk': <Blog: Cheddar Talk>, 'Django Weblog': <Blog: Django Weblog>}
If you pass ``in_bulk()`` an empty list, you'll get an empty dictionary.
+.. versionchanged:: 3.2
+
+ Using a distinct field was allowed.
+
``iterator()``
~~~~~~~~~~~~~~
diff --git a/docs/releases/3.2.txt b/docs/releases/3.2.txt
index c7219c759b..d29a582d74 100644
--- a/docs/releases/3.2.txt
+++ b/docs/releases/3.2.txt
@@ -286,6 +286,10 @@ Models
* The new :class:`~django.db.models.functions.Collate` function allows
filtering and ordering by specified database collations.
+* The ``field_name`` argument of :meth:`.QuerySet.in_bulk()` now accepts
+ distinct fields if there's only one field specified in
+ :meth:`.QuerySet.distinct`.
+
Pagination
~~~~~~~~~~
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index 3d8a801933..9094542808 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -207,6 +207,24 @@ class LookupTests(TestCase):
with self.assertRaisesMessage(ValueError, msg):
Article.objects.in_bulk([self.au1], field_name='author')
+ @skipUnlessDBFeature('can_distinct_on_fields')
+ def test_in_bulk_distinct_field(self):
+ self.assertEqual(
+ Article.objects.order_by('headline').distinct('headline').in_bulk(
+ [self.a1.headline, self.a5.headline],
+ field_name='headline',
+ ),
+ {self.a1.headline: self.a1, self.a5.headline: self.a5},
+ )
+
+ @skipUnlessDBFeature('can_distinct_on_fields')
+ def test_in_bulk_multiple_distinct_field(self):
+ msg = "in_bulk()'s field_name must be a unique field but 'pub_date' isn't."
+ with self.assertRaisesMessage(ValueError, msg):
+ Article.objects.order_by('headline', 'pub_date').distinct(
+ 'headline', 'pub_date',
+ ).in_bulk(field_name='pub_date')
+
@isolate_apps('lookup')
def test_in_bulk_non_unique_meta_constaint(self):
class Model(models.Model):