mirror of
				https://github.com/encode/django-rest-framework.git
				synced 2025-11-04 09:57:55 +03:00 
			
		
		
		
	Fix UniqueTogetherValidator with field sources (#7086)
* Add failing tests for unique_together+source * Fix UniqueTogetherValidator source handling * Fix read-only+default+source handling * Update test to use functional serializer * Test UniqueTogetherValidator error+source
This commit is contained in:
		
							parent
							
								
									f744da74d2
								
							
						
					
					
						commit
						236667b717
					
				| 
						 | 
					@ -448,7 +448,7 @@ class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
 | 
				
			||||||
                default = field.get_default()
 | 
					                default = field.get_default()
 | 
				
			||||||
            except SkipField:
 | 
					            except SkipField:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            defaults[field.field_name] = default
 | 
					            defaults[field.source] = default
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return defaults
 | 
					        return defaults
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ class UniqueTogetherValidator:
 | 
				
			||||||
        missing_items = {
 | 
					        missing_items = {
 | 
				
			||||||
            field_name: self.missing_message
 | 
					            field_name: self.missing_message
 | 
				
			||||||
            for field_name in self.fields
 | 
					            for field_name in self.fields
 | 
				
			||||||
            if field_name not in attrs
 | 
					            if serializer.fields[field_name].source not in attrs
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if missing_items:
 | 
					        if missing_items:
 | 
				
			||||||
            raise ValidationError(missing_items, code='required')
 | 
					            raise ValidationError(missing_items, code='required')
 | 
				
			||||||
| 
						 | 
					@ -115,17 +115,23 @@ class UniqueTogetherValidator:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Filter the queryset to all instances matching the given attributes.
 | 
					        Filter the queryset to all instances matching the given attributes.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        # field names => field sources
 | 
				
			||||||
 | 
					        sources = [
 | 
				
			||||||
 | 
					            serializer.fields[field_name].source
 | 
				
			||||||
 | 
					            for field_name in self.fields
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # 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 serializer.instance is not None:
 | 
					        if serializer.instance is not None:
 | 
				
			||||||
            for field_name in self.fields:
 | 
					            for source in sources:
 | 
				
			||||||
                if field_name not in attrs:
 | 
					                if source not in attrs:
 | 
				
			||||||
                    attrs[field_name] = getattr(serializer.instance, field_name)
 | 
					                    attrs[source] = getattr(serializer.instance, source)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Determine the filter keyword arguments and filter the queryset.
 | 
					        # Determine the filter keyword arguments and filter the queryset.
 | 
				
			||||||
        filter_kwargs = {
 | 
					        filter_kwargs = {
 | 
				
			||||||
            field_name: attrs[field_name]
 | 
					            source: attrs[source]
 | 
				
			||||||
            for field_name in self.fields
 | 
					            for source in sources
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return qs_filter(queryset, **filter_kwargs)
 | 
					        return qs_filter(queryset, **filter_kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -301,6 +301,49 @@ class TestUniquenessTogetherValidation(TestCase):
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_read_only_fields_with_default_and_source(self):
 | 
				
			||||||
 | 
					        class ReadOnlySerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					            name = serializers.CharField(source='race_name', default='test', read_only=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                model = UniquenessTogetherModel
 | 
				
			||||||
 | 
					                fields = ['name', 'position']
 | 
				
			||||||
 | 
					                validators = [
 | 
				
			||||||
 | 
					                    UniqueTogetherValidator(
 | 
				
			||||||
 | 
					                        queryset=UniquenessTogetherModel.objects.all(),
 | 
				
			||||||
 | 
					                        fields=['name', 'position']
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        serializer = ReadOnlySerializer(data={'position': 1})
 | 
				
			||||||
 | 
					        assert serializer.is_valid(raise_exception=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_writeable_fields_with_source(self):
 | 
				
			||||||
 | 
					        class WriteableSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					            name = serializers.CharField(source='race_name')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                model = UniquenessTogetherModel
 | 
				
			||||||
 | 
					                fields = ['name', 'position']
 | 
				
			||||||
 | 
					                validators = [
 | 
				
			||||||
 | 
					                    UniqueTogetherValidator(
 | 
				
			||||||
 | 
					                        queryset=UniquenessTogetherModel.objects.all(),
 | 
				
			||||||
 | 
					                        fields=['name', 'position']
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        serializer = WriteableSerializer(data={'name': 'test', 'position': 1})
 | 
				
			||||||
 | 
					        assert serializer.is_valid(raise_exception=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Validation error should use seriazlier field name, not source
 | 
				
			||||||
 | 
					        serializer = WriteableSerializer(data={'position': 1})
 | 
				
			||||||
 | 
					        assert not serializer.is_valid()
 | 
				
			||||||
 | 
					        assert serializer.errors == {
 | 
				
			||||||
 | 
					            'name': [
 | 
				
			||||||
 | 
					                'This field is required.'
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_allow_explict_override(self):
 | 
					    def test_allow_explict_override(self):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Ensure validators can be explicitly removed..
 | 
					        Ensure validators can be explicitly removed..
 | 
				
			||||||
| 
						 | 
					@ -357,13 +400,9 @@ 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)
 | 
					        serializer = UniquenessTogetherSerializer(instance=self.instance)
 | 
				
			||||||
        validator = UniqueTogetherValidator(queryset, fields=('race_name',
 | 
					        validator = UniqueTogetherValidator(queryset, fields=('race_name',
 | 
				
			||||||
                                                              'position'))
 | 
					                                                              'position'))
 | 
				
			||||||
        validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
 | 
					        validator.filter_queryset(attrs=data, queryset=queryset, serializer=serializer)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user