diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 28d06f25c..29c43762a 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -229,6 +229,10 @@ Corresponds to `django.forms.fields.IPAddressField` and `django.forms.fields.Gen - `protocol` Limits valid inputs to the specified protocol. Accepted values are 'both' (default), 'IPv4' or 'IPv6'. Matching is case insensitive. - `unpack_ipv4` Unpacks IPv4 mapped addresses like ::ffff:192.0.2.1. If this option is enabled that address would be unpacked to 192.0.2.1. Default is disabled. Can only be used when protocol is set to 'both'. +## PasswordField + +A write-only field that doesn't trim whitespaces by default and sets the appropiate input style. + --- # Numeric fields diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 41d6105ca..60e7d5f8c 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -876,6 +876,19 @@ class IPAddressField(CharField): return super(IPAddressField, self).to_internal_value(data) +class PasswordField(CharField): + """ + A write-only subclass of Charfield that doesn't trim whitespaces by default + and sets the appropiate input style. + """ + + def __init__(self, **kwargs): + kwargs.setdefault('write_only', True) + kwargs.setdefault('trim_whitespace', False) + kwargs.setdefault('style', {'input_type': 'password'}) + super(PasswordField, self).__init__(**kwargs) + + # Number types... class IntegerField(Field): diff --git a/tests/test_fields.py b/tests/test_fields.py index 968c41d3f..2cb85ea18 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -805,6 +805,39 @@ class TestIPv6AddressField(FieldValues): field = serializers.IPAddressField(protocol='IPv6') +class TestPasswordField(FieldValues): + """ + Valid and invalid values for `PasswordField`. + """ + valid_inputs = { + ' password with spaces ': ' password with spaces ', + ' ': ' ' + } + invalid_inputs = { + '': ['This field may not be blank.'] + } + outputs = { + ' password with spaces ': ' password with spaces ', + ' ': ' ' + } + field = serializers.PasswordField() + + def test_trim_whitespace_default(self): + field = serializers.PasswordField() + assert field.to_internal_value(' abc ') == ' abc ' + + def test_trim_whitespace_enabled(self): + field = serializers.PasswordField(trim_whitespace=True) + assert field.to_internal_value(' abc ') == 'abc' + + def test_disallow_blank_with_trim_whitespace(self): + field = serializers.PasswordField(allow_blank=False, trim_whitespace=True) + + with pytest.raises(serializers.ValidationError) as exc_info: + field.run_validation(' ') + assert exc_info.value.detail == ['This field may not be blank.'] + + class TestFilePathField(FieldValues): """ Valid and invalid values for `FilePathField`