Skip to content

Commit cc921a2

Browse files
rpkilbysigvef
authored andcommitted
Followup to set_context removal (encode#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 7b96468 commit cc921a2

File tree

4 files changed

+25
-28
lines changed

4 files changed

+25
-28
lines changed

rest_framework/fields.py

Lines changed: 7 additions & 7 deletions
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

Lines changed: 9 additions & 16 deletions
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

Lines changed: 3 additions & 4 deletions
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

Lines changed: 6 additions & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy