From 10168a31844d48ddd2b634d7e5bc54cd95ac2f68 Mon Sep 17 00:00:00 2001 From: "Peter J. Farrell" Date: Wed, 22 Jan 2020 08:19:22 -0600 Subject: [PATCH] Added support for DictField / ListField (#59) --- HISTORY.md | 5 +++ service_objects/fields.py | 68 +++++++++++++++++++++++++++++++++++++++ tests/test_fields.py | 59 +++++++++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c31d377..e0a9b68 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,6 +2,11 @@ ## Unreleased +**Features and Improvements** + +* Added support for DictField - peterfarrell +* Added support for ListField - peterfarrell + ## 0.6.0 (2020-01-03) **Features and Improvements** diff --git a/service_objects/fields.py b/service_objects/fields.py index b56c2d3..b2e4412 100644 --- a/service_objects/fields.py +++ b/service_objects/fields.py @@ -208,3 +208,71 @@ def clean(self, values): self.check_type(value) self.check_unsaved(value) return values + + +class DictField(forms.Field): + """ + A field for :class:`Service` that accepts a dictionary: + + class PDFGenerate(Service): + context = DictField() + + process(self): + context = self.cleaned_data['context'] + + PDFGenerate.execute({ + 'context': {'a': 1, 'b': 2} + }) + + + """ + error_required = _("Input is required. Expected dict but got %(value)r.") + error_type = _("Input needs to be of type dict.") + + def clean(self, value): + if not value and value is not False: + if self.required: + raise ValidationError(self.error_required % { + 'value': value + }) + else: + return {} + + if not isinstance(value, dict): + raise ValidationError(self.error_type) + + return value + + +class ListField(forms.Field): + """ + A field for :class:`Service` that accepts a list: + + class EmailWelcomeMessage(Service): + emails = ListField() + + process(self): + emails = self.cleaned_data['emails'] + + EmailWelcomeMessage.execute({ + 'emails': ['blue@test.com', 'red@test.com'] + }) + + + """ + error_required = _("Input is required. Expected list but got %(value)r.") + error_type = _("Input needs to be of type list.") + + def clean(self, value): + if not value and value is not False: + if self.required: + raise ValidationError(self.error_required % { + 'value': value + }) + else: + return {} + + if not isinstance(value, list): + raise ValidationError(self.error_type) + + return value diff --git a/tests/test_fields.py b/tests/test_fields.py index 2ad4c4d..d409025 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -2,7 +2,8 @@ from django.core.exceptions import ValidationError -from service_objects.fields import MultipleFormField, ModelField, MultipleModelField +from service_objects.fields import MultipleFormField, ModelField, MultipleModelField, \ + DictField, ListField from tests.forms import FooForm from tests.models import FooModel, BarModel, NonModel @@ -181,7 +182,7 @@ def test_multiple_valid(self): self.assertEqual(objects, cleaned_data) - def test_is_requred(self): + def test_is_required(self): f = MultipleModelField(FooModel) with self.assertRaises(ValidationError) as cm: f.clean(None) @@ -189,7 +190,59 @@ def test_is_requred(self): self.assertEqual( 'Input is required expected list of models but got None.', cm.exception.message) - def test_is_not_requred(self): + def test_is_not_required(self): f = MultipleModelField(FooModel, required=False) # should not raise any exception f.clean(None) + + +class DictFieldTest(TestCase): + def test_is_required(self): + dict_field = DictField(required=True) + + with self.assertRaises(ValidationError) as cm: + dict_field.clean(None) + + self.assertEqual( + 'Input is required. Expected dict but got None.', cm.exception.message) + + def test_is_not_required(self): + dict_field = DictField(required=False) + + # should not raise any exception + dict_field.clean(None) + + def test_invalid_type(self): + dict_field = DictField(required=True) + + with self.assertRaises(ValidationError) as cm: + dict_field.clean('string test') + + self.assertEqual( + 'Input needs to be of type dict.', cm.exception.message) + + +class ListFieldTest(TestCase): + def test_is_required(self): + list_field = ListField(required=True) + + with self.assertRaises(ValidationError) as cm: + list_field.clean(None) + + self.assertEqual( + 'Input is required. Expected list but got None.', cm.exception.message) + + def test_is_not_required(self): + list_field = ListField(required=False) + + # should not raise any exception + list_field.clean(None) + + def test_invalid_type(self): + list_field = ListField(required=True) + + with self.assertRaises(ValidationError) as cm: + list_field.clean('string test') + + self.assertEqual( + 'Input needs to be of type list.', cm.exception.message)