From 2ce30ddc7af092d7cfaf87f74bdd03cd871c0f75 Mon Sep 17 00:00:00 2001 From: Michael K Date: Mon, 22 Jan 2018 16:03:08 +0100 Subject: [PATCH 1/2] Test that validator's context is not used by another serializer This is a failing test for encode/django-rest-framework#5760 --- tests/test_validators.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_validators.py b/tests/test_validators.py index 62126ddb3..10791ac39 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -341,6 +341,28 @@ class TestUniquenessTogetherValidation(TestCase): validator.filter_queryset(attrs=data, queryset=queryset) assert queryset.called_with == {'race_name': 'bar', 'position': 1} + def test_validator_instances_with_context_are_not_used_twice(self): + """ + Every instance of a serializer should use unique instances of validators + when calling `set_context`. + """ + def data(): + return {'race_name': 'example', 'position': 3} + + def check_validators(serializer): + for validator in serializer.validators: + assert not hasattr(validator, 'instance') + + serializer = UniquenessTogetherSerializer(self.instance, data=data()) + check_validators(serializer) + serializer.run_validators({}) + check_validators(serializer) + + another_serializer = UniquenessTogetherSerializer(data=data()) + check_validators(another_serializer) + another_serializer.run_validators(data()) + check_validators(another_serializer) + # Tests for `UniqueForDateValidator` # ---------------------------------- From a1322ca4419b6ce9c5eaedd2ad540d5e3aa8861b Mon Sep 17 00:00:00 2001 From: Michael K Date: Mon, 22 Jan 2018 16:11:56 +0100 Subject: [PATCH 2/2] Prevent validator's context to be used by another serializer Fixes encode/django-rest-framework#5760 --- rest_framework/fields.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a336528e8..40d94ecdd 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -251,6 +251,7 @@ class CreateOnlyDefault(object): def set_context(self, serializer_field): self.is_update = serializer_field.parent.instance is not None if callable(self.default) and hasattr(self.default, 'set_context') and not self.is_update: + self.default = copy.deepcopy(self.default) self.default.set_context(serializer_field) def __call__(self): @@ -475,6 +476,7 @@ class Field(object): raise SkipField() if callable(self.default): if hasattr(self.default, 'set_context'): + self.default = copy.deepcopy(self.default) self.default.set_context(self) return self.default() return self.default @@ -532,6 +534,7 @@ class Field(object): errors = [] for validator in self.validators: if hasattr(validator, 'set_context'): + validator = copy.deepcopy(validator) validator.set_context(self) try: