Skip to content

Commit b72027f

Browse files
Group queries for SlugRelatedField many serializers
1 parent d6ca95f commit b72027f

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

rest_framework/relations.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from urllib import parse
55

66
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
7-
from django.db.models import Manager
7+
from django.db.models import F, Manager
88
from django.db.models.query import QuerySet
99
from django.urls import NoReverseMatch, Resolver404, get_script_prefix, resolve
1010
from django.utils.encoding import smart_str, uri_to_iri
@@ -458,15 +458,26 @@ def __init__(self, slug_field=None, **kwargs):
458458
self.slug_field = slug_field
459459
super().__init__(**kwargs)
460460

461-
def to_internal_value(self, data):
461+
def to_many_internal_value(self, data):
462462
queryset = self.get_queryset()
463463
try:
464-
return queryset.get(**{self.slug_field: data})
465-
except ObjectDoesNotExist:
466-
self.fail('does_not_exist', slug_name=self.slug_field, value=smart_str(data))
464+
result = (
465+
queryset
466+
.filter(**{self.slug_field + "__in": data})
467+
.annotate(_slug_field_value=F(self.slug_field))
468+
.all()
469+
)
470+
slugs = [item._slug_field_value for item in result]
471+
for item in data:
472+
if item not in slugs:
473+
self.fail('does_not_exist', slug_name=self.slug_field, value=smart_str(item))
474+
return result
467475
except (TypeError, ValueError):
468476
self.fail('invalid')
469477

478+
def to_internal_value(self, data):
479+
return self.to_many_internal_value([data])[0]
480+
470481
def to_representation(self, obj):
471482
slug = self.slug_field
472483
if "__" in slug:

tests/test_relations_slug.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ def test_reverse_foreign_key_create(self):
174174
]
175175
assert serializer.data == expected
176176

177+
def test_reverse_foreign_key_create_grouped_queries(self):
178+
data = {'id': 3, 'name': 'target-3', 'sources': ['source-1', 'source-3']}
179+
serializer = ForeignKeyTargetSerializer(data=data)
180+
with self.assertNumQueries(1):
181+
assert serializer.is_valid()
182+
177183
def test_foreign_key_update_with_invalid_null(self):
178184
data = {'id': 1, 'name': 'source-1', 'target': None}
179185
instance = ForeignKeySource.objects.get(pk=1)

tests/utils.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def all(self):
3535
return list(self.items)
3636

3737
def filter(self, **lookup):
38-
return MockQueryset(
38+
return MockQueryset([
3939
item
4040
for item in self.items
4141
if all([
@@ -44,7 +44,13 @@ def filter(self, **lookup):
4444
else attrgetter(key.replace('__', '.'))(item) == value
4545
for key, value in lookup.items()
4646
])
47-
)
47+
])
48+
49+
def annotate(self, **kwargs):
50+
for key, value in kwargs.items():
51+
for item in self.items:
52+
setattr(item, key, attrgetter(value.name.replace('__', '.'))(item))
53+
return self
4854

4955

5056
class BadType:

0 commit comments

Comments
 (0)