Skip to content

Commit

Permalink
Fix regression of RegexField. (#4490)
Browse files Browse the repository at this point in the history

* Don't deepcopy 'regex' arguments, instead treat as immutable.
  • Loading branch information
tomchristie committed Sep 15, 2016
1 parent a68b37d commit 4655501
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 9 deletions.
21 changes: 12 additions & 9 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,8 @@ class SkipField(Exception):
pass


REGEX_TYPE = type(re.compile(''))

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_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
Expand Down Expand Up @@ -581,16 +583,17 @@ def __deepcopy__(self, memo):
When cloning fields we instantiate using the arguments it was
originally created with, rather than copying the complete state.
"""
args = copy.deepcopy(self._args)
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.
# Treat regexes and validators as immutable.
# See https://github.com/tomchristie/django-rest-framework/issues/1954
validators = kwargs.pop('validators', None)
kwargs = copy.deepcopy(kwargs)
if validators is not None:
kwargs['validators'] = validators
# and https://github.com/tomchristie/django-rest-framework/pull/4489
args = [
copy.deepcopy(item) if not isinstance(item, REGEX_TYPE) else item
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)

def __repr__(self):
Expand Down
15 changes: 15 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import os
import re
import uuid
from decimal import Decimal

Expand Down Expand Up @@ -590,6 +591,20 @@ class TestRegexField(FieldValues):
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):
"""
Valid and invalid values for `SlugField`.
Expand Down
14 changes: 14 additions & 0 deletions tests/test_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import unicode_literals

import pickle
import re

import pytest

Expand Down Expand Up @@ -337,3 +338,16 @@ def test_default_should_not_be_included_on_partial_update(self):
assert serializer.is_valid()
assert serializer.validated_data == {'integer': 456}
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 == {}

0 comments on commit 4655501

Please sign in to comment.