From 19bdfda49af2986d0a27e829272f732bb159903f Mon Sep 17 00:00:00 2001 From: Xiao Hanyu Date: Wed, 20 Jan 2016 11:28:18 +0800 Subject: [PATCH] Fix #3844, refine validator for fields with kwargs When serializers has fields with something like `source=user.email`, the uniqueness validator should check `email` field instead of `user`, cause `user` is a model object. --- rest_framework/validators.py | 6 +++--- tests/test_validators.py | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/rest_framework/validators.py b/rest_framework/validators.py index a21f67e60..83ad6f7d8 100644 --- a/rest_framework/validators.py +++ b/rest_framework/validators.py @@ -35,7 +35,7 @@ class UniqueValidator(object): """ # Determine the underlying model field name. This may not be the # same as the serializer field name if `source=<>` is set. - self.field_name = serializer_field.source_attrs[0] + self.field_name = serializer_field.source_attrs[-1] # Determine the existing instance, if this is an update operation. self.instance = getattr(serializer_field.parent, 'instance', None) @@ -174,8 +174,8 @@ class BaseUniqueForValidator(object): """ # Determine the underlying model field names. These may not be the # same as the serializer field names if `source=<>` is set. - self.field_name = serializer.fields[self.field].source_attrs[0] - self.date_field_name = serializer.fields[self.date_field].source_attrs[0] + self.field_name = serializer.fields[self.field].source_attrs[-1] + self.date_field_name = serializer.fields[self.date_field].source_attrs[-1] # Determine the existing instance, if this is an update operation. self.instance = getattr(serializer, 'instance', None) diff --git a/tests/test_validators.py b/tests/test_validators.py index acaaf5743..67156f31c 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -4,6 +4,7 @@ from django.db import models from django.test import TestCase from rest_framework import serializers +from rest_framework.validators import UniqueValidator def dedent(blocktext): @@ -22,6 +23,20 @@ class UniquenessSerializer(serializers.ModelSerializer): model = UniquenessModel +class RelatedModel(models.Model): + user = models.OneToOneField(UniquenessModel, on_delete=models.CASCADE) + email = models.CharField(unique=True, max_length=80) + + +class RelatedModelSerializer(serializers.ModelSerializer): + username = serializers.CharField(source='user.username', + validators=[UniqueValidator(queryset=UniquenessModel.objects.all())]) # NOQA + + class Meta: + model = RelatedModel + fields = ('username', 'email') + + class AnotherUniquenessModel(models.Model): code = models.IntegerField(unique=True) @@ -73,6 +88,16 @@ class TestUniquenessValidation(TestCase): self.assertEqual( AnotherUniquenessModel._meta.get_field('code').validators, []) + def test_related_model_is_unique(self): + data = {'username': 'existing', 'email': 'new-email@example.com'} + rs = RelatedModelSerializer(data=data) + self.assertFalse(rs.is_valid()) + self.assertEqual(rs.errors, + {'username': ['This field must be unique.']}) + data = {'username': 'new-username', 'email': 'new-email@example.com'} + rs = RelatedModelSerializer(data=data) + self.assertTrue(rs.is_valid()) + # Tests for `UniqueTogetherValidator` # -----------------------------------