Skip to content

Commit de9f1d5

Browse files
rpkilbytomchristie
authored andcommitted
Followup to set_context removal (#7076)
* Raise framework-specific deprecation warnings - Use `RemovedInDRF313Warning` instead of DeprecationWarning - Update to follow deprecation policy * Pass serializer instead of model to validator The `UniqueTogetherValidator` may need to access attributes on the serializer instead of just the model instance. For example, this is useful for handling field sources. * Fix framework deprecation warning in test * Remove outdated validator attribute
1 parent ebcd931 commit de9f1d5

File tree

4 files changed

+25
-28
lines changed

4 files changed

+25
-28
lines changed

rest_framework/fields.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from django.utils.translation import gettext_lazy as _
3131
from pytz.exceptions import InvalidTimeError
3232

33-
from rest_framework import ISO_8601
33+
from rest_framework import ISO_8601, RemovedInDRF313Warning
3434
from rest_framework.compat import ProhibitNullCharactersValidator
3535
from rest_framework.exceptions import ErrorDetail, ValidationError
3636
from rest_framework.settings import api_settings
@@ -263,10 +263,10 @@ def __call__(self, serializer_field):
263263
if hasattr(self.default, 'set_context'):
264264
warnings.warn(
265265
"Method `set_context` on defaults is deprecated and will "
266-
"no longer be called starting with 3.12. Instead set "
266+
"no longer be called starting with 3.13. Instead set "
267267
"`requires_context = True` on the class, and accept the "
268268
"context as an additional argument.",
269-
DeprecationWarning, stacklevel=2
269+
RemovedInDRF313Warning, stacklevel=2
270270
)
271271
self.default.set_context(self)
272272

@@ -502,10 +502,10 @@ def get_default(self):
502502
if hasattr(self.default, 'set_context'):
503503
warnings.warn(
504504
"Method `set_context` on defaults is deprecated and will "
505-
"no longer be called starting with 3.12. Instead set "
505+
"no longer be called starting with 3.13. Instead set "
506506
"`requires_context = True` on the class, and accept the "
507507
"context as an additional argument.",
508-
DeprecationWarning, stacklevel=2
508+
RemovedInDRF313Warning, stacklevel=2
509509
)
510510
self.default.set_context(self)
511511

@@ -576,10 +576,10 @@ def run_validators(self, value):
576576
if hasattr(validator, 'set_context'):
577577
warnings.warn(
578578
"Method `set_context` on validators is deprecated and will "
579-
"no longer be called starting with 3.12. Instead set "
579+
"no longer be called starting with 3.13. Instead set "
580580
"`requires_context = True` on the class, and accept the "
581581
"context as an additional argument.",
582-
DeprecationWarning, stacklevel=2
582+
RemovedInDRF313Warning, stacklevel=2
583583
)
584584
validator.set_context(self)
585585

rest_framework/validators.py

+9-16
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class UniqueValidator:
4141

4242
def __init__(self, queryset, message=None, lookup='exact'):
4343
self.queryset = queryset
44-
self.serializer_field = None
4544
self.message = message or self.message
4645
self.lookup = lookup
4746

@@ -94,15 +93,14 @@ class UniqueTogetherValidator:
9493
def __init__(self, queryset, fields, message=None):
9594
self.queryset = queryset
9695
self.fields = fields
97-
self.serializer_field = None
9896
self.message = message or self.message
9997

100-
def enforce_required_fields(self, attrs, instance):
98+
def enforce_required_fields(self, attrs, serializer):
10199
"""
102100
The `UniqueTogetherValidator` always forces an implied 'required'
103101
state on the fields it applies to.
104102
"""
105-
if instance is not None:
103+
if serializer.instance is not None:
106104
return
107105

108106
missing_items = {
@@ -113,16 +111,16 @@ def enforce_required_fields(self, attrs, instance):
113111
if missing_items:
114112
raise ValidationError(missing_items, code='required')
115113

116-
def filter_queryset(self, attrs, queryset, instance):
114+
def filter_queryset(self, attrs, queryset, serializer):
117115
"""
118116
Filter the queryset to all instances matching the given attributes.
119117
"""
120118
# If this is an update, then any unprovided field should
121119
# have it's value set based on the existing instance attribute.
122-
if instance is not None:
120+
if serializer.instance is not None:
123121
for field_name in self.fields:
124122
if field_name not in attrs:
125-
attrs[field_name] = getattr(instance, field_name)
123+
attrs[field_name] = getattr(serializer.instance, field_name)
126124

127125
# Determine the filter keyword arguments and filter the queryset.
128126
filter_kwargs = {
@@ -141,13 +139,10 @@ def exclude_current_instance(self, attrs, queryset, instance):
141139
return queryset
142140

143141
def __call__(self, attrs, serializer):
144-
# Determine the existing instance, if this is an update operation.
145-
instance = getattr(serializer, 'instance', None)
146-
147-
self.enforce_required_fields(attrs, instance)
142+
self.enforce_required_fields(attrs, serializer)
148143
queryset = self.queryset
149-
queryset = self.filter_queryset(attrs, queryset, instance)
150-
queryset = self.exclude_current_instance(attrs, queryset, instance)
144+
queryset = self.filter_queryset(attrs, queryset, serializer)
145+
queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)
151146

152147
# Ignore validation if any field is None
153148
checked_values = [
@@ -207,13 +202,11 @@ def __call__(self, attrs, serializer):
207202
# same as the serializer field names if `source=<>` is set.
208203
field_name = serializer.fields[self.field].source_attrs[-1]
209204
date_field_name = serializer.fields[self.date_field].source_attrs[-1]
210-
# Determine the existing instance, if this is an update operation.
211-
instance = getattr(serializer, 'instance', None)
212205

213206
self.enforce_required_fields(attrs)
214207
queryset = self.queryset
215208
queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name)
216-
queryset = self.exclude_current_instance(attrs, queryset, instance)
209+
queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)
217210
if qs_exists(queryset):
218211
message = self.message.format(date_field=self.date_field)
219212
raise ValidationError({

tests/test_fields.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -565,11 +565,10 @@ def test_create_only_default_callable_sets_context(self):
565565
on the callable if possible
566566
"""
567567
class TestCallableDefault:
568-
def set_context(self, serializer_field):
569-
self.field = serializer_field
568+
requires_context = True
570569

571-
def __call__(self):
572-
return "success" if hasattr(self, 'field') else "failure"
570+
def __call__(self, field=None):
571+
return "success" if field is not None else "failure"
573572

574573
class TestSerializer(serializers.Serializer):
575574
context_set = serializers.CharField(default=serializers.CreateOnlyDefault(TestCallableDefault()))

tests/test_validators.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -357,11 +357,16 @@ class MockQueryset:
357357
def filter(self, **kwargs):
358358
self.called_with = kwargs
359359

360+
class MockSerializer:
361+
def __init__(self, instance):
362+
self.instance = instance
363+
360364
data = {'race_name': 'bar'}
361365
queryset = MockQueryset()
366+
serializer = MockSerializer(instance=self.instance)
362367
validator = UniqueTogetherValidator(queryset, fields=('race_name',
363368
'position'))
364-
validator.filter_queryset(attrs=data, queryset=queryset, instance=self.instance)
369+
validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
365370
assert queryset.called_with == {'race_name': 'bar', 'position': 1}
366371

367372

0 commit comments

Comments
 (0)