From 8f84c06c50b93885e7c02aa05dad78ab316f51fc Mon Sep 17 00:00:00 2001 From: Jerome Leclanche Date: Sun, 8 Jul 2018 23:56:26 +0300 Subject: [PATCH] Switch to using native ProhibitNullCharactersValidator instead --- docs/api-guide/fields.md | 1 - rest_framework/compat.py | 5 +++++ rest_framework/fields.py | 11 ++++++----- tests/test_fields.py | 13 +++++-------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 1da58d2ee..8d25d6c78 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -153,7 +153,6 @@ Corresponds to `django.db.models.fields.CharField` or `django.db.models.fields.T - `max_length` - Validates that the input contains no more than this number of characters. - `min_length` - Validates that the input contains no fewer than this number of characters. - `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`. -- `allow_null_bytes` - If set to `False`, strings containing NULL bytes will be rejected. You may want to set this to `False` if the string is saved to a database. Defaults to `True`. - `trim_whitespace` - If set to `True` then leading and trailing whitespace is trimmed. Defaults to `True`. The `allow_null` option is also available for string fields, although its usage is discouraged in favor of `allow_blank`. It is valid to set both `allow_blank=True` and `allow_null=True`, but doing so means that there will be two differing types of empty value permissible for string representations, which can lead to data inconsistencies and subtle application bugs. diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 28e1f1f59..9773cd2b4 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -22,6 +22,11 @@ except ImportError: RegexURLResolver as URLResolver, ) +try: + from django.core.validators import ProhibitNullCharactersValidator # noqa +except ImportError: + ProhibitNullCharactersValidator = None + def get_original_route(urlpattern): """ diff --git a/rest_framework/fields.py b/rest_framework/fields.py index fa6771f45..fc2a0255f 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -34,7 +34,8 @@ from pytz.exceptions import InvalidTimeError from rest_framework import ISO_8601 from rest_framework.compat import ( MaxLengthValidator, MaxValueValidator, MinLengthValidator, - MinValueValidator, unicode_repr, unicode_to_repr + MinValueValidator, ProhibitNullCharactersValidator, unicode_repr, + unicode_to_repr ) from rest_framework.exceptions import ErrorDetail, ValidationError from rest_framework.settings import api_settings @@ -755,13 +756,11 @@ class CharField(Field): 'blank': _('This field may not be blank.'), 'max_length': _('Ensure this field has no more than {max_length} characters.'), 'min_length': _('Ensure this field has at least {min_length} characters.'), - 'nulls': _('This field may not include NULL bytes.'), } initial = '' def __init__(self, **kwargs): self.allow_blank = kwargs.pop('allow_blank', False) - self.allow_null_bytes = kwargs.pop('allow_null_bytes', True) self.trim_whitespace = kwargs.pop('trim_whitespace', True) self.max_length = kwargs.pop('max_length', None) self.min_length = kwargs.pop('min_length', None) @@ -779,6 +778,10 @@ class CharField(Field): self.validators.append( MinLengthValidator(self.min_length, message=message)) + # ProhibitNullCharactersValidator is None on Django < 2.0 + if ProhibitNullCharactersValidator is not None: + self.validators.append(ProhibitNullCharactersValidator()) + def run_validation(self, data=empty): # Test for the empty string here so that it does not get validated, # and so that subclasses do not need to handle it explicitly @@ -787,8 +790,6 @@ class CharField(Field): if not self.allow_blank: self.fail('blank') return '' - if not self.allow_null_bytes and '\0' in six.text_type(data): - self.fail('nulls') return super(CharField, self).run_validation(data) def to_internal_value(self, data): diff --git a/tests/test_fields.py b/tests/test_fields.py index 067a2e4b4..85f4af027 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -15,6 +15,7 @@ from django.utils.timezone import activate, deactivate, override, utc import rest_framework from rest_framework import exceptions, serializers +from rest_framework.compat import ProhibitNullCharactersValidator from rest_framework.fields import DjangoImageField, is_simple_callable try: @@ -718,19 +719,15 @@ class TestCharField(FieldValues): field.run_validation(' ') assert exc_info.value.detail == ['This field may not be blank.'] - def test_allow_null_bytes(self): - field = serializers.CharField(allow_null_bytes=True) - for value in ('\0', 'foo\0', '\0foo', 'foo\0foo'): - field.run_validation(value) - - def test_disallow_null_bytes(self): - field = serializers.CharField(allow_null_bytes=False) + @pytest.mark.skipif(ProhibitNullCharactersValidator is None, reason="Skipped on Django < 2.0") + def test_null_bytes(self): + field = serializers.CharField() for value in ('\0', 'foo\0', '\0foo', 'foo\0foo'): with pytest.raises(serializers.ValidationError) as exc_info: field.run_validation(value) assert exc_info.value.detail == [ - serializers.CharField.default_error_messages['nulls'] + 'Null characters are not allowed.' ]