Skip to content

Commit 070cff5

Browse files
authored
Drop set_context() (#7062)
* Do not persist the context in validators Fixes #5760 * Drop set_context() in favour of 'requires_context = True'
1 parent 9325c3f commit 070cff5

File tree

5 files changed

+131
-93
lines changed

5 files changed

+131
-93
lines changed

docs/api-guide/fields.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,19 @@ If set, this gives the default value that will be used for the field if no input
5050

5151
The `default` is not applied during partial update operations. In the partial update case only fields that are provided in the incoming data will have a validated value returned.
5252

53-
May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `set_context` method, that will be called each time before getting the value with the field instance as only argument. This works the same way as for [validators](validators.md#using-set_context).
53+
May be set to a function or other callable, in which case the value will be evaluated each time it is used. When called, it will receive no arguments. If the callable has a `requires_context = True` attribute, then the serializer field will be passed as an argument.
54+
55+
For example:
56+
57+
class CurrentUserDefault:
58+
"""
59+
May be applied as a `default=...` value on a serializer field.
60+
Returns the current user.
61+
"""
62+
requires_context = True
63+
64+
def __call__(self, serializer_field):
65+
return serializer_field.context['request'].user
5466

5567
When serializing the instance, default will be used if the object attribute or dictionary key is not present in the instance.
5668

docs/api-guide/validators.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -291,13 +291,17 @@ To write a class-based validator, use the `__call__` method. Class-based validat
291291
message = 'This field must be a multiple of %d.' % self.base
292292
raise serializers.ValidationError(message)
293293

294-
#### Using `set_context()`
294+
#### Accessing the context
295295

296-
In some advanced cases you might want a validator to be passed the serializer field it is being used with as additional context. You can do so by declaring a `set_context` method on a class-based validator.
296+
In some advanced cases you might want a validator to be passed the serializer
297+
field it is being used with as additional context. You can do so by setting
298+
a `requires_context = True` attribute on the validator. The `__call__` method
299+
will then be called with the `serializer_field`
300+
or `serializer` as an additional argument.
297301

298-
def set_context(self, serializer_field):
299-
# Determine if this is an update or a create operation.
300-
# In `__call__` we can then use that information to modify the validation behavior.
301-
self.is_update = serializer_field.parent.instance is not None
302+
requires_context = True
303+
304+
def __call__(self, value, serializer_field):
305+
...
302306

303307
[cite]: https://docs.djangoproject.com/en/stable/ref/validators/

rest_framework/fields.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import inspect
66
import re
77
import uuid
8+
import warnings
89
from collections import OrderedDict
910
from collections.abc import Mapping
1011

@@ -249,31 +250,41 @@ class CreateOnlyDefault:
249250
for create operations, but that do not return any value for update
250251
operations.
251252
"""
253+
requires_context = True
254+
252255
def __init__(self, default):
253256
self.default = default
254257

255-
def set_context(self, serializer_field):
256-
self.is_update = serializer_field.parent.instance is not None
257-
if callable(self.default) and hasattr(self.default, 'set_context') and not self.is_update:
258-
self.default.set_context(serializer_field)
259-
260-
def __call__(self):
261-
if self.is_update:
258+
def __call__(self, serializer_field):
259+
is_update = serializer_field.parent.instance is not None
260+
if is_update:
262261
raise SkipField()
263262
if callable(self.default):
264-
return self.default()
263+
if hasattr(self.default, 'set_context'):
264+
warnings.warn(
265+
"Method `set_context` on defaults is deprecated and will "
266+
"no longer be called starting with 3.12. Instead set "
267+
"`requires_context = True` on the class, and accept the "
268+
"context as an additional argument.",
269+
DeprecationWarning, stacklevel=2
270+
)
271+
self.default.set_context(self)
272+
273+
if getattr(self.default, 'requires_context', False):
274+
return self.default(serializer_field)
275+
else:
276+
return self.default()
265277
return self.default
266278

267279
def __repr__(self):
268280
return '%s(%s)' % (self.__class__.__name__, repr(self.default))
269281

270282

271283
class CurrentUserDefault:
272-
def set_context(self, serializer_field):
273-
self.user = serializer_field.context['request'].user
284+
requires_context = True
274285

275-
def __call__(self):
276-
return self.user
286+
def __call__(self, serializer_field):
287+
return serializer_field.context['request'].user
277288

278289
def __repr__(self):
279290
return '%s()' % self.__class__.__name__
@@ -489,8 +500,20 @@ def get_default(self):
489500
raise SkipField()
490501
if callable(self.default):
491502
if hasattr(self.default, 'set_context'):
503+
warnings.warn(
504+
"Method `set_context` on defaults is deprecated and will "
505+
"no longer be called starting with 3.12. Instead set "
506+
"`requires_context = True` on the class, and accept the "
507+
"context as an additional argument.",
508+
DeprecationWarning, stacklevel=2
509+
)
492510
self.default.set_context(self)
493-
return self.default()
511+
512+
if getattr(self.default, 'requires_context', False):
513+
return self.default(self)
514+
else:
515+
return self.default()
516+
494517
return self.default
495518

496519
def validate_empty_values(self, data):
@@ -551,10 +574,20 @@ def run_validators(self, value):
551574
errors = []
552575
for validator in self.validators:
553576
if hasattr(validator, 'set_context'):
577+
warnings.warn(
578+
"Method `set_context` on validators is deprecated and will "
579+
"no longer be called starting with 3.12. Instead set "
580+
"`requires_context = True` on the class, and accept the "
581+
"context as an additional argument.",
582+
DeprecationWarning, stacklevel=2
583+
)
554584
validator.set_context(self)
555585

556586
try:
557-
validator(value)
587+
if getattr(validator, 'requires_context', False):
588+
validator(value, self)
589+
else:
590+
validator(value)
558591
except ValidationError as exc:
559592
# If the validation error contains a mapping of fields to
560593
# errors then simply raise it immediately rather than

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