Skip to content

Commit

Permalink
Switch to using native ProhibitNullCharactersValidator instead
Browse files Browse the repository at this point in the history
  • Loading branch information
jleclanche committed Sep 7, 2018
1 parent bd72f84 commit 8f84c06
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 14 deletions.
1 change: 0 additions & 1 deletion docs/api-guide/fields.md
Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions rest_framework/compat.py
Expand Up @@ -22,6 +22,11 @@
RegexURLResolver as URLResolver,
)

try:
from django.core.validators import ProhibitNullCharactersValidator # noqa
except ImportError:
ProhibitNullCharactersValidator = None


def get_original_route(urlpattern):
"""
Expand Down
11 changes: 6 additions & 5 deletions rest_framework/fields.py
Expand Up @@ -34,7 +34,8 @@
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
Expand Down Expand Up @@ -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)
Expand All @@ -779,6 +778,10 @@ def __init__(self, **kwargs):
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
Expand All @@ -787,8 +790,6 @@ def run_validation(self, data=empty):
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):
Expand Down
13 changes: 5 additions & 8 deletions tests/test_fields.py
Expand Up @@ -15,6 +15,7 @@

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:
Expand Down Expand Up @@ -718,19 +719,15 @@ def test_disallow_blank_with_trim_whitespace(self):
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.'
]


Expand Down

0 comments on commit 8f84c06

Please sign in to comment.