Fix regression of RegexField. (#4490)

* Don't deepcopy 'regex' arguments, instead treat as immutable.
This commit is contained in:
Tom Christie 2016-09-15 12:44:45 +01:00 committed by GitHub
parent a68b37d8bc
commit 4655501d51
3 changed files with 41 additions and 9 deletions

View File

@ -252,6 +252,8 @@ class SkipField(Exception):
pass pass
REGEX_TYPE = type(re.compile(''))
NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`' NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`'
NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`' NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`'
NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`' NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
@ -581,16 +583,17 @@ class Field(object):
When cloning fields we instantiate using the arguments it was When cloning fields we instantiate using the arguments it was
originally created with, rather than copying the complete state. originally created with, rather than copying the complete state.
""" """
args = copy.deepcopy(self._args) # Treat regexes and validators as immutable.
kwargs = dict(self._kwargs)
# Bit ugly, but we need to special case 'validators' as Django's
# RegexValidator does not support deepcopy.
# We treat validator callables as immutable objects.
# See https://github.com/tomchristie/django-rest-framework/issues/1954 # See https://github.com/tomchristie/django-rest-framework/issues/1954
validators = kwargs.pop('validators', None) # and https://github.com/tomchristie/django-rest-framework/pull/4489
kwargs = copy.deepcopy(kwargs) args = [
if validators is not None: copy.deepcopy(item) if not isinstance(item, REGEX_TYPE) else item
kwargs['validators'] = validators for item in self._args
]
kwargs = {
key: (copy.deepcopy(value) if (key not in ('validators', 'regex')) else value)
for key, value in self._kwargs.items()
}
return self.__class__(*args, **kwargs) return self.__class__(*args, **kwargs)
def __repr__(self): def __repr__(self):

View File

@ -1,5 +1,6 @@
import datetime import datetime
import os import os
import re
import uuid import uuid
from decimal import Decimal from decimal import Decimal
@ -590,6 +591,20 @@ class TestRegexField(FieldValues):
field = serializers.RegexField(regex='[a-z][0-9]') field = serializers.RegexField(regex='[a-z][0-9]')
class TestiCompiledRegexField(FieldValues):
"""
Valid and invalid values for `RegexField`.
"""
valid_inputs = {
'a9': 'a9',
}
invalid_inputs = {
'A9': ["This value does not match the required pattern."]
}
outputs = {}
field = serializers.RegexField(regex=re.compile('[a-z][0-9]'))
class TestSlugField(FieldValues): class TestSlugField(FieldValues):
""" """
Valid and invalid values for `SlugField`. Valid and invalid values for `SlugField`.

View File

@ -2,6 +2,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import pickle import pickle
import re
import pytest import pytest
@ -337,3 +338,16 @@ class TestDefaultInclusions:
assert serializer.is_valid() assert serializer.is_valid()
assert serializer.validated_data == {'integer': 456} assert serializer.validated_data == {'integer': 456}
assert serializer.errors == {} assert serializer.errors == {}
class TestSerializerValidationWithCompiledRegexField:
def setup(self):
class ExampleSerializer(serializers.Serializer):
name = serializers.RegexField(re.compile(r'\d'), required=True)
self.Serializer = ExampleSerializer
def test_validation_success(self):
serializer = self.Serializer(data={'name': '2'})
assert serializer.is_valid()
assert serializer.validated_data == {'name': '2'}
assert serializer.errors == {}