mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 01:47:59 +03:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									ebcd93163a
								
							
						
					
					
						commit
						de9f1d56c4
					
				| 
						 | 
					@ -30,7 +30,7 @@ from django.utils.timezone import utc
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from pytz.exceptions import InvalidTimeError
 | 
					from pytz.exceptions import InvalidTimeError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rest_framework import ISO_8601
 | 
					from rest_framework import ISO_8601, RemovedInDRF313Warning
 | 
				
			||||||
from rest_framework.compat import ProhibitNullCharactersValidator
 | 
					from rest_framework.compat import ProhibitNullCharactersValidator
 | 
				
			||||||
from rest_framework.exceptions import ErrorDetail, ValidationError
 | 
					from rest_framework.exceptions import ErrorDetail, ValidationError
 | 
				
			||||||
from rest_framework.settings import api_settings
 | 
					from rest_framework.settings import api_settings
 | 
				
			||||||
| 
						 | 
					@ -263,10 +263,10 @@ class CreateOnlyDefault:
 | 
				
			||||||
            if hasattr(self.default, 'set_context'):
 | 
					            if hasattr(self.default, 'set_context'):
 | 
				
			||||||
                warnings.warn(
 | 
					                warnings.warn(
 | 
				
			||||||
                    "Method `set_context` on defaults is deprecated and will "
 | 
					                    "Method `set_context` on defaults is deprecated and will "
 | 
				
			||||||
                    "no longer be called starting with 3.12. Instead set "
 | 
					                    "no longer be called starting with 3.13. Instead set "
 | 
				
			||||||
                    "`requires_context = True` on the class, and accept the "
 | 
					                    "`requires_context = True` on the class, and accept the "
 | 
				
			||||||
                    "context as an additional argument.",
 | 
					                    "context as an additional argument.",
 | 
				
			||||||
                    DeprecationWarning, stacklevel=2
 | 
					                    RemovedInDRF313Warning, stacklevel=2
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                self.default.set_context(self)
 | 
					                self.default.set_context(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -502,10 +502,10 @@ class Field:
 | 
				
			||||||
            if hasattr(self.default, 'set_context'):
 | 
					            if hasattr(self.default, 'set_context'):
 | 
				
			||||||
                warnings.warn(
 | 
					                warnings.warn(
 | 
				
			||||||
                    "Method `set_context` on defaults is deprecated and will "
 | 
					                    "Method `set_context` on defaults is deprecated and will "
 | 
				
			||||||
                    "no longer be called starting with 3.12. Instead set "
 | 
					                    "no longer be called starting with 3.13. Instead set "
 | 
				
			||||||
                    "`requires_context = True` on the class, and accept the "
 | 
					                    "`requires_context = True` on the class, and accept the "
 | 
				
			||||||
                    "context as an additional argument.",
 | 
					                    "context as an additional argument.",
 | 
				
			||||||
                    DeprecationWarning, stacklevel=2
 | 
					                    RemovedInDRF313Warning, stacklevel=2
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                self.default.set_context(self)
 | 
					                self.default.set_context(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -576,10 +576,10 @@ class Field:
 | 
				
			||||||
            if hasattr(validator, 'set_context'):
 | 
					            if hasattr(validator, 'set_context'):
 | 
				
			||||||
                warnings.warn(
 | 
					                warnings.warn(
 | 
				
			||||||
                    "Method `set_context` on validators is deprecated and will "
 | 
					                    "Method `set_context` on validators is deprecated and will "
 | 
				
			||||||
                    "no longer be called starting with 3.12. Instead set "
 | 
					                    "no longer be called starting with 3.13. Instead set "
 | 
				
			||||||
                    "`requires_context = True` on the class, and accept the "
 | 
					                    "`requires_context = True` on the class, and accept the "
 | 
				
			||||||
                    "context as an additional argument.",
 | 
					                    "context as an additional argument.",
 | 
				
			||||||
                    DeprecationWarning, stacklevel=2
 | 
					                    RemovedInDRF313Warning, stacklevel=2
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                validator.set_context(self)
 | 
					                validator.set_context(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,6 @@ class UniqueValidator:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, queryset, message=None, lookup='exact'):
 | 
					    def __init__(self, queryset, message=None, lookup='exact'):
 | 
				
			||||||
        self.queryset = queryset
 | 
					        self.queryset = queryset
 | 
				
			||||||
        self.serializer_field = None
 | 
					 | 
				
			||||||
        self.message = message or self.message
 | 
					        self.message = message or self.message
 | 
				
			||||||
        self.lookup = lookup
 | 
					        self.lookup = lookup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,15 +93,14 @@ class UniqueTogetherValidator:
 | 
				
			||||||
    def __init__(self, queryset, fields, message=None):
 | 
					    def __init__(self, queryset, fields, message=None):
 | 
				
			||||||
        self.queryset = queryset
 | 
					        self.queryset = queryset
 | 
				
			||||||
        self.fields = fields
 | 
					        self.fields = fields
 | 
				
			||||||
        self.serializer_field = None
 | 
					 | 
				
			||||||
        self.message = message or self.message
 | 
					        self.message = message or self.message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def enforce_required_fields(self, attrs, instance):
 | 
					    def enforce_required_fields(self, attrs, serializer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        The `UniqueTogetherValidator` always forces an implied 'required'
 | 
					        The `UniqueTogetherValidator` always forces an implied 'required'
 | 
				
			||||||
        state on the fields it applies to.
 | 
					        state on the fields it applies to.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if instance is not None:
 | 
					        if serializer.instance is not None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        missing_items = {
 | 
					        missing_items = {
 | 
				
			||||||
| 
						 | 
					@ -113,16 +111,16 @@ class UniqueTogetherValidator:
 | 
				
			||||||
        if missing_items:
 | 
					        if missing_items:
 | 
				
			||||||
            raise ValidationError(missing_items, code='required')
 | 
					            raise ValidationError(missing_items, code='required')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def filter_queryset(self, attrs, queryset, instance):
 | 
					    def filter_queryset(self, attrs, queryset, serializer):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Filter the queryset to all instances matching the given attributes.
 | 
					        Filter the queryset to all instances matching the given attributes.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        # If this is an update, then any unprovided field should
 | 
					        # If this is an update, then any unprovided field should
 | 
				
			||||||
        # have it's value set based on the existing instance attribute.
 | 
					        # have it's value set based on the existing instance attribute.
 | 
				
			||||||
        if instance is not None:
 | 
					        if serializer.instance is not None:
 | 
				
			||||||
            for field_name in self.fields:
 | 
					            for field_name in self.fields:
 | 
				
			||||||
                if field_name not in attrs:
 | 
					                if field_name not in attrs:
 | 
				
			||||||
                    attrs[field_name] = getattr(instance, field_name)
 | 
					                    attrs[field_name] = getattr(serializer.instance, field_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Determine the filter keyword arguments and filter the queryset.
 | 
					        # Determine the filter keyword arguments and filter the queryset.
 | 
				
			||||||
        filter_kwargs = {
 | 
					        filter_kwargs = {
 | 
				
			||||||
| 
						 | 
					@ -141,13 +139,10 @@ class UniqueTogetherValidator:
 | 
				
			||||||
        return queryset
 | 
					        return queryset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __call__(self, attrs, serializer):
 | 
					    def __call__(self, attrs, serializer):
 | 
				
			||||||
        # Determine the existing instance, if this is an update operation.
 | 
					        self.enforce_required_fields(attrs, serializer)
 | 
				
			||||||
        instance = getattr(serializer, 'instance', None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.enforce_required_fields(attrs, instance)
 | 
					 | 
				
			||||||
        queryset = self.queryset
 | 
					        queryset = self.queryset
 | 
				
			||||||
        queryset = self.filter_queryset(attrs, queryset, instance)
 | 
					        queryset = self.filter_queryset(attrs, queryset, serializer)
 | 
				
			||||||
        queryset = self.exclude_current_instance(attrs, queryset, instance)
 | 
					        queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Ignore validation if any field is None
 | 
					        # Ignore validation if any field is None
 | 
				
			||||||
        checked_values = [
 | 
					        checked_values = [
 | 
				
			||||||
| 
						 | 
					@ -207,13 +202,11 @@ class BaseUniqueForValidator:
 | 
				
			||||||
        # same as the serializer field names if `source=<>` is set.
 | 
					        # same as the serializer field names if `source=<>` is set.
 | 
				
			||||||
        field_name = serializer.fields[self.field].source_attrs[-1]
 | 
					        field_name = serializer.fields[self.field].source_attrs[-1]
 | 
				
			||||||
        date_field_name = serializer.fields[self.date_field].source_attrs[-1]
 | 
					        date_field_name = serializer.fields[self.date_field].source_attrs[-1]
 | 
				
			||||||
        # Determine the existing instance, if this is an update operation.
 | 
					 | 
				
			||||||
        instance = getattr(serializer, 'instance', None)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.enforce_required_fields(attrs)
 | 
					        self.enforce_required_fields(attrs)
 | 
				
			||||||
        queryset = self.queryset
 | 
					        queryset = self.queryset
 | 
				
			||||||
        queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name)
 | 
					        queryset = self.filter_queryset(attrs, queryset, field_name, date_field_name)
 | 
				
			||||||
        queryset = self.exclude_current_instance(attrs, queryset, instance)
 | 
					        queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)
 | 
				
			||||||
        if qs_exists(queryset):
 | 
					        if qs_exists(queryset):
 | 
				
			||||||
            message = self.message.format(date_field=self.date_field)
 | 
					            message = self.message.format(date_field=self.date_field)
 | 
				
			||||||
            raise ValidationError({
 | 
					            raise ValidationError({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -565,11 +565,10 @@ class TestCreateOnlyDefault:
 | 
				
			||||||
        on the callable if possible
 | 
					        on the callable if possible
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        class TestCallableDefault:
 | 
					        class TestCallableDefault:
 | 
				
			||||||
            def set_context(self, serializer_field):
 | 
					            requires_context = True
 | 
				
			||||||
                self.field = serializer_field
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            def __call__(self):
 | 
					            def __call__(self, field=None):
 | 
				
			||||||
                return "success" if hasattr(self, 'field') else "failure"
 | 
					                return "success" if field is not None else "failure"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class TestSerializer(serializers.Serializer):
 | 
					        class TestSerializer(serializers.Serializer):
 | 
				
			||||||
            context_set = serializers.CharField(default=serializers.CreateOnlyDefault(TestCallableDefault()))
 | 
					            context_set = serializers.CharField(default=serializers.CreateOnlyDefault(TestCallableDefault()))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -357,11 +357,16 @@ class TestUniquenessTogetherValidation(TestCase):
 | 
				
			||||||
            def filter(self, **kwargs):
 | 
					            def filter(self, **kwargs):
 | 
				
			||||||
                self.called_with = kwargs
 | 
					                self.called_with = kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class MockSerializer:
 | 
				
			||||||
 | 
					            def __init__(self, instance):
 | 
				
			||||||
 | 
					                self.instance = instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        data = {'race_name': 'bar'}
 | 
					        data = {'race_name': 'bar'}
 | 
				
			||||||
        queryset = MockQueryset()
 | 
					        queryset = MockQueryset()
 | 
				
			||||||
 | 
					        serializer = MockSerializer(instance=self.instance)
 | 
				
			||||||
        validator = UniqueTogetherValidator(queryset, fields=('race_name',
 | 
					        validator = UniqueTogetherValidator(queryset, fields=('race_name',
 | 
				
			||||||
                                                              'position'))
 | 
					                                                              'position'))
 | 
				
			||||||
        validator.filter_queryset(attrs=data, queryset=queryset, instance=self.instance)
 | 
					        validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
 | 
				
			||||||
        assert queryset.called_with == {'race_name': 'bar', 'position': 1}
 | 
					        assert queryset.called_with == {'race_name': 'bar', 'position': 1}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user