From 016f97314f5f86e0280b8fe15649799c5a21a847 Mon Sep 17 00:00:00 2001 From: ChampChamp Date: Mon, 6 Apr 2026 17:56:19 +0800 Subject: [PATCH] Fix #9607: Batch queryset lookup for PrimaryKeyRelatedField with many=True --- rest_framework/relations.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rest_framework/relations.py b/rest_framework/relations.py index 4409bce77c..f08d49f670 100644 --- a/rest_framework/relations.py +++ b/rest_framework/relations.py @@ -262,6 +262,28 @@ def to_internal_value(self, data): except (TypeError, ValueError): self.fail('incorrect_type', data_type=type(data).__name__) + def many_to_internal_value(self, data): + pks = [] + for item in data: + if self.pk_field is not None: + item = self.pk_field.to_internal_value(item) + if isinstance(item, bool): + self.fail('incorrect_type', data_type=type(item).__name__) + pks.append(item) + queryset = self.get_queryset() + try: + objs = {str(obj.pk): obj for obj in queryset.filter(pk__in=pks)} + except (ValueError, TypeError): + # Fall back to per-item validation to surface correct error messages + return [self.to_internal_value(pk) for pk in pks] + result = [] + for pk in pks: + obj = objs.get(str(pk)) + if obj is None: + self.fail('does_not_exist', pk_value=pk) + result.append(obj) + return result + def to_representation(self, value): if self.pk_field is not None: return self.pk_field.to_representation(value.pk) @@ -524,6 +546,9 @@ def to_internal_value(self, data): if not self.allow_empty and len(data) == 0: self.fail('empty') + if hasattr(self.child_relation, 'many_to_internal_value'): + return self.child_relation.many_to_internal_value(data) + return [ self.child_relation.to_internal_value(item) for item in data