Skip to content

Commit

Permalink
Fixed Issue #23 (#25) 🍋
Browse files Browse the repository at this point in the history
* Add min and max length to FieldList and FormFieldList

* Trigger CI/CD

* Updated docs 📝

* Undo Trigger CI/CD

Co-authored-by: bumblebebee211196 <[email protected]>
  • Loading branch information
bumblebee211196 and bumblebebee211196 authored Jul 12, 2021
1 parent 0d0c7d5 commit 3d34fd2
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 3 deletions.
29 changes: 28 additions & 1 deletion django_api_forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,20 @@ def has_changed(self, initial, data):

class FieldList(Field):
default_error_messages = {
'max_length': _('Ensure this list has at most %(max)d values (it has %(length)d).'),
'min_length': _('Ensure this list has at least %(max)d values (it has %(length)d).'),
'not_field': _('Invalid Field type passed into FieldList!'),
'not_list': _('This field needs to be a list of objects!'),
}

def __init__(self, field, **kwargs):
def __init__(self, field, min_length=None, max_length=None, **kwargs):
super().__init__(**kwargs)

if not isinstance(field, Field):
raise RuntimeError(self.error_messages['not_field'])

self._min_length = min_length
self._max_length = max_length
self._field = field

def to_python(self, value) -> typing.List:
Expand All @@ -59,6 +63,14 @@ def to_python(self, value) -> typing.List:
if not isinstance(value, list):
raise ValidationError(self.error_messages['not_list'], code='not_list')

if self._min_length is not None and len(value) < self._min_length:
params = {'max': self._min_length, 'length': len(value)}
raise ValidationError(self.error_messages['min_length'], code='min_length', params=params)

if self._max_length is not None and len(value) > self._max_length:
params = {'max': self._max_length, 'length': len(value)}
raise ValidationError(self.error_messages['max_length'], code='max_length', params=params)

result = []
errors = []

Expand Down Expand Up @@ -97,7 +109,14 @@ def to_python(self, value) -> typing.Union[typing.Dict, None]:


class FormFieldList(FormField, IgnoreFillMixin):
def __init__(self, form: typing.Type, min_length=None, max_length=None, **kwargs):
self._min_length = min_length
self._max_length = max_length
super().__init__(form, **kwargs)

default_error_messages = {
'max_length': _('Ensure this list has at most %(max)d values (it has %(length)d).'),
'min_length': _('Ensure this list has at least %(max)d values (it has %(length)d).'),
'not_list': _('This field needs to be a list of objects!')
}

Expand All @@ -108,6 +127,14 @@ def to_python(self, value):
if not isinstance(value, list):
raise ValidationError(self.error_messages['not_list'], code='not_list')

if self._min_length is not None and len(value) < self._min_length:
params = {'max': self._min_length, 'length': len(value)}
raise ValidationError(self.error_messages['min_length'], code='min_length', params=params)

if self._max_length is not None and len(value) > self._max_length:
params = {'max': self._max_length, 'length': len(value)}
raise ValidationError(self.error_messages['max_length'], code='max_length', params=params)

result = []
errors = []

Expand Down
8 changes: 6 additions & 2 deletions docs/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ This field is used to parse list of primitive values (like strings or numbers).
check `FormFieldList`.

- Normalizes to: A Python list
- Error message keys: `not_field`, `not_list`
- Error message keys: `not_field`, `not_list`, `min_length`, `max_length`
- Required arguments:
- `field`: Instance of a form field representing children
- `min_length`: Minimum length of field size in integer (optional)
- `max_length`: Maximum length of field size in integer (optional)

**JSON example**

Expand Down Expand Up @@ -130,9 +132,11 @@ class AlbumForm(Form):
Field used for embedded objects represented as another API form.

- Normalizes to: A Python list of dictionaries
- Error message keys: `not_list`
- Error message keys: `not_list`, `min_length`, `max_length`
- Required arguments:
- `form`: Type of a nested form
- `min_length`: Minimum length of field size in integer (optional)
- `max_length`: Maximum length of field size in integer (optional)

**JSON example**

Expand Down
48 changes: 48 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,30 @@ def test_fieldlist_required_false(self):
for empty_val in EMPTY_VALUES:
self.assertEqual([], field_list.clean(empty_val))

def test_min_length(self):
form_field_list = FieldList(field=fields.IntegerField(), min_length=2)

# TEST: valid input
valid_val = [1, 2]
self.assertEqual(valid_val, form_field_list.clean(valid_val))

# TEST: invalid input (values more than the defined max length)
valid_val = [1]
with self.assertRaises(ValidationError):
form_field_list.clean(valid_val)

def test_max_length(self):
form_field_list = FieldList(field=fields.IntegerField(), max_length=3)

# TEST: valid input
valid_val = [1, 2, 3]
self.assertEqual(valid_val, form_field_list.clean(valid_val))

# TEST: invalid input (values more than the defined max length)
valid_val = [1, 2, 3, 4]
with self.assertRaises(ValidationError):
form_field_list.clean(valid_val)


class FormFieldTests(SimpleTestCase):
class TestFormWithRequiredField(Form):
Expand Down Expand Up @@ -272,6 +296,30 @@ def test_formfieldlist_required_false(self):
# TEST: required=False - [] allowed
self.assertEqual([], form_field_list.clean([]))

def test_min_length(self):
form_field_list = FormFieldList(form=self.TestFormWithRequiredField, min_length=2)

# TEST: valid input
valid_val = [{'number': 1}, {'number': 2}]
self.assertEqual(valid_val, form_field_list.clean(valid_val))

# TEST: invalid input (values more than the defined max length)
valid_val = [{'number': 1}]
with self.assertRaises(ValidationError):
form_field_list.clean(valid_val)

def test_max_length(self):
form_field_list = FormFieldList(form=self.TestFormWithRequiredField, max_length=3)

# TEST: valid input
valid_val = [{'number': 1}, {'number': 2}, {'number': 0}]
self.assertEqual(valid_val, form_field_list.clean(valid_val))

# TEST: invalid input (values more than the defined max length)
valid_val = [{'number': 1}, {'number': 2}, {'number': 0}, {'number': 4}]
with self.assertRaises(ValidationError):
form_field_list.clean(valid_val)


class EnumFieldTests(SimpleTestCase):
class Color(Enum):
Expand Down

0 comments on commit 3d34fd2

Please sign in to comment.