diff --git a/rest_framework/fields.py b/rest_framework/fields.py index e4be54751d..1434116d82 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -320,7 +320,7 @@ class Field: default_empty_html = empty initial = None - def __init__(self, read_only=False, write_only=False, + def __init__(self, *, read_only=False, write_only=False, required=None, default=empty, initial=empty, source=None, label=None, help_text=None, style=None, error_messages=None, validators=None, allow_null=False): @@ -1161,14 +1161,14 @@ class DateTimeField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, default_timezone=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, default_timezone=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats if default_timezone is not None: self.timezone = default_timezone - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def enforce_timezone(self, value): """ @@ -1247,12 +1247,12 @@ class DateField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS) @@ -1313,12 +1313,12 @@ class TimeField(Field): } datetime_parser = datetime.datetime.strptime - def __init__(self, format=empty, input_formats=None, *args, **kwargs): + def __init__(self, format=empty, input_formats=None, **kwargs): if format is not empty: self.format = format if input_formats is not None: self.input_formats = input_formats - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, value): input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS) @@ -1468,9 +1468,9 @@ class MultipleChoiceField(ChoiceField): } default_empty_html = [] - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.allow_empty = kwargs.pop('allow_empty', True) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def get_value(self, dictionary): if self.field_name not in dictionary: @@ -1527,12 +1527,12 @@ class FileField(Field): 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.max_length = kwargs.pop('max_length', None) self.allow_empty_file = kwargs.pop('allow_empty_file', False) if 'use_url' in kwargs: self.use_url = kwargs.pop('use_url') - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, data): try: @@ -1576,9 +1576,9 @@ class ImageField(FileField): ), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def to_internal_value(self, data): # Image validation is a bit grungy, so we'll just outright @@ -1593,8 +1593,8 @@ def to_internal_value(self, data): # Composite field types... class _UnvalidatedField(Field): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) self.allow_blank = True self.allow_null = True @@ -1615,7 +1615,7 @@ class ListField(Field): 'max_length': _('Ensure this field has no more than {max_length} elements.') } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.allow_empty = kwargs.pop('allow_empty', True) self.max_length = kwargs.pop('max_length', None) @@ -1627,7 +1627,7 @@ def __init__(self, *args, **kwargs): "Remove `source=` from the field declaration." ) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) self.child.bind(field_name='', parent=self) if self.max_length is not None: message = lazy_format(self.error_messages['max_length'], max_length=self.max_length) @@ -1692,7 +1692,7 @@ class DictField(Field): 'empty': _('This dictionary may not be empty.'), } - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.child = kwargs.pop('child', copy.deepcopy(self.child)) self.allow_empty = kwargs.pop('allow_empty', True) @@ -1702,7 +1702,7 @@ def __init__(self, *args, **kwargs): "Remove `source=` from the field declaration." ) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) self.child.bind(field_name='', parent=self) def get_value(self, dictionary): @@ -1751,8 +1751,8 @@ def run_child_validation(self, data): class HStoreField(DictField): child = CharField(allow_blank=True, allow_null=True) - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, **kwargs): + super().__init__(**kwargs) assert isinstance(self.child, CharField), ( "The `child` argument must be an instance of `CharField`, " "as the hstore extension stores values as strings." @@ -1767,11 +1767,11 @@ class JSONField(Field): # Workaround for isinstance calls when importing the field isn't possible _is_jsonfield = True - def __init__(self, *args, **kwargs): + def __init__(self, **kwargs): self.binary = kwargs.pop('binary', False) self.encoder = kwargs.pop('encoder', None) self.decoder = kwargs.pop('decoder', None) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) def get_value(self, dictionary): if html.is_html_input(dictionary) and self.field_name in dictionary: diff --git a/tests/test_fields.py b/tests/test_fields.py index 78a9effb8c..dd4b7904a3 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1986,6 +1986,11 @@ def test_collection_types_are_invalid_input(self): field.to_internal_value(input_value) assert exc_info.value.detail == ['Expected a list of items but got type "dict".'] + def test_constructor_misuse_raises(self): + # Test that `ListField` can only be instantiated with keyword arguments + with pytest.raises(TypeError): + serializers.ListField(serializers.CharField()) + class TestNestedListField(FieldValues): """