Explorar el Código

New test cases

theenglishway (time) hace 6 años
padre
commit
3d7ccd1b69
Se han modificado 3 ficheros con 146 adiciones y 92 borrados
  1. 2 71
      pydantic_form/pydantic_form.py
  2. 93 19
      tests/conftest.py
  3. 51 2
      tests/test_process.py

+ 2 - 71
pydantic_form/pydantic_form.py

@@ -24,74 +24,5 @@ class PydanticForm(Form):
         super().__init__(*args, **kwargs)
         self._errors = {}
 
-    @staticmethod
-    def _schema_to_form_key(schema_key):
-        return schema_key
-
-    @staticmethod
-    def _add_field_error(form, field, value):
-        form.errors.update({field: value})
-        field = getattr(form, field)
-        if isinstance(field, FormField):
-            field.form._errors = value
-        elif isinstance(field, FieldList):
-            raise NotImplementedError()
-        else:
-            field.errors = value
-
-    def _update_field_errors(self, field_key, value):
-        if len(field_key) > 1:
-            path, latest = field_key[:-1], field_key[-1]
-            if len(path) == 1:
-                self._add_field_error(self, path[0], {latest: value})
-                sub_form = getattr(self, path[0]).form
-                self._add_field_error(sub_form, latest, value)
-        else:
-            self._add_field_error(self, field_key[0], value)
-
-    def _process_data_for_schema(self, data):
-        """Process data form for pydantic schema"""
-        return data
-
-    def _form_field_from_baked_field(self, baked_field_key):
-        """Get the form field to fill and the value from a field in the baked
-        instance"""
-        value = getattr(self._baked_instance, baked_field_key)
-        return getattr(self, baked_field_key), value
-
-    def validate(self):
-        """Validate the form, relying on Pydantic"""
-        try:
-            data = self._process_data_for_schema(self.data)
-            self._baked_instance = self._schema(**data)
-
-            # Fill the values of every sub-forms (if any)
-            for k, v in self._baked_instance.fields.items():
-                if issubclass(v.type_, BaseModel):
-                    field_attr, value = self._form_field_from_baked_field(k)
-                    if isinstance(field_attr, FieldList):
-                        field_attr._baked_instance = value
-                    else:
-                        field_attr.form._baked_instance = value
-
-            return True
-        except ValidationError as e:
-            errors_dict = defaultdict(list)
-            for e in e.errors():
-                errors_dict[self._schema_to_form_key(e['loc'])].append(e)
-
-            for k, v in errors_dict.items():
-                self._update_field_errors(k, v)
-            return False
-
-    def populate_obj(self, obj):
-        """Populate `obj` with data from the form
-
-        :param obj: the object to populate """
-        for k, v in self._baked_instance.dict().items():
-            setattr(obj, k, v)
-
-    @property
-    def errors(self):
-        """Errors in the form, in the format of a dict"""
-        return self._errors
+    def process(self, formdata=None, obj=None, data=None, **kwargs):
+        super().process(formdata, obj, data, **kwargs)

+ 93 - 19
tests/conftest.py

@@ -7,8 +7,34 @@ from pydantic_form import PydanticForm
 from pydantic import BaseModel
 
 
-ScenarioClasses = namedtuple("ScenarioClasses", ['wtf_form', 'pydantic_form', 'schema', 'data_factory'])
-ScenarioInstances = namedtuple("ScenarioInstances", ['wtf_form', 'pydantic_form', 'formdata', 'data'])
+ScenarioClasses = namedtuple(
+    "ScenarioClasses",
+    ['wtf_form', 'pydantic_form', 'schema', 'data_factory']
+)
+
+ScenarioInstances = namedtuple(
+    "ScenarioInstances",
+    [
+        'wtf', 'wtf_formdata',
+        'pydantic', 'pydantic_formdata',
+        'formdata', 'data'
+    ]
+)
+
+
+@pytest.fixture(scope="session")
+def instance_factory():
+    def _factory(scenario_classes, data):
+        formdata = ImmutableMultiDict(data)
+        return ScenarioInstances(
+            scenario_classes.wtf_form(data=data),
+            scenario_classes.wtf_form(formdata=formdata),
+            scenario_classes.pydantic_form(data=data),
+            scenario_classes.pydantic_form(formdata=formdata),
+            formdata,
+            data
+        )
+    return _factory
 
 
 class SimpleSchema(BaseModel):
@@ -27,31 +53,79 @@ class SimpleForm(SimpleWTForm, PydanticForm):
     _schema = SimpleSchema
 
 
-class SimpleFormDataFactory(factory.Factory):
+@pytest.fixture(scope="session")
+def scenario_classes_simple():
+    return ScenarioClasses(SimpleWTForm, SimpleForm, SimpleSchema, SimpleDataFactory)
+
+
+class SimpleDataFactory(factory.Factory):
     class Meta:
         model = dict
 
     integer = factory.Faker('pyint')
     string = factory.Faker('pystr')
 
+class SimpleBadDataFactory(factory.Factory):
+    class Meta:
+        model = dict
 
-@pytest.fixture(scope="session")
-def scenario_classes_simple():
-    return ScenarioClasses(SimpleWTForm, SimpleForm, SimpleSchema, SimpleFormDataFactory)
+    integer = factory.Faker('pystr')
+    string = factory.Faker('pyint')
 
 
-@pytest.fixture(scope="session")
-def instance_factory():
-    def _factory(scenario_classes):
-        data = scenario_classes.data_factory()
-        return ScenarioInstances(
-            scenario_classes.wtf_form(data=data),
-            scenario_classes.pydantic_form(data=data),
-            ImmutableMultiDict(data),
-            data
-        )
-    return _factory
+@pytest.fixture
+def scenario_simple(instance_factory, scenario_classes_simple):
+    return instance_factory(scenario_classes_simple, SimpleDataFactory())
+
+
+@pytest.fixture
+def scenario_simple_bad(instance_factory, scenario_classes_simple):
+    return instance_factory(scenario_classes_simple, SimpleBadDataFactory())
+
+
+
+class NestedSchema(BaseModel):
+    integer: int
+    nested: SimpleSchema
+
+
+class NestedWTForm(Form):
+    _schema = NestedSchema
+
+    integer = fields.IntegerField()
+    nested = fields.FormField(form_class=SimpleForm)
+
+
+class NestedForm(NestedWTForm, PydanticForm):
+    _schema = NestedSchema
+
+
+class NestedDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pyint')
+    nested = factory.SubFactory(SimpleDataFactory)
+
+
+class NestedBadDataFactory(factory.Factory):
+    class Meta:
+        model = dict
+
+    integer = factory.Faker('pystr')
+    nested = factory.SubFactory(SimpleBadDataFactory)
+
 
 @pytest.fixture(scope="session")
-def scenario_instance_simple(instance_factory, scenario_classes_simple):
-    return instance_factory(scenario_classes_simple)
+def scenario_classes_nested():
+    return ScenarioClasses(NestedWTForm, NestedForm, NestedSchema, NestedDataFactory)
+
+
+@pytest.fixture
+def scenario_nested(instance_factory, scenario_classes_nested):
+    return instance_factory(scenario_classes_nested, NestedDataFactory())
+
+
+@pytest.fixture
+def scenario_nested_bad(instance_factory, scenario_classes_nested):
+    return instance_factory(scenario_classes_nested, NestedBadDataFactory())

+ 51 - 2
tests/test_process.py

@@ -1,4 +1,53 @@
-def test_process(scenario_instance_simple):
-    print(scenario_instance_simple)
+import pytest
 
 
+@pytest.fixture
+def scenario(request):
+    return request.getfixturevalue(request.param)
+
+VALID_SCENARIOS = [
+    'scenario_simple',
+    'scenario_nested'
+]
+INVALID_SCENARIOS = [
+    'scenario_simple_bad',
+    'scenario_nested_bad',
+]
+
+@pytest.mark.parametrize(
+    'scenario',
+    VALID_SCENARIOS + INVALID_SCENARIOS,
+    indirect=True
+)
+def test_process(scenario):
+    assert scenario.wtf.data == scenario.pydantic.data
+    assert scenario.wtf_formdata.data == scenario.pydantic_formdata.data
+
+
+@pytest.mark.parametrize(
+    'scenario',
+    VALID_SCENARIOS,
+    indirect=True
+)
+def test_validate(scenario):
+    assert scenario.wtf.validate()
+    assert scenario.wtf_formdata.validate()
+    assert scenario.pydantic.validate()
+    assert scenario.pydantic_formdata.validate()
+
+
+@pytest.mark.parametrize(
+    'scenario',
+    INVALID_SCENARIOS,
+    indirect=True
+)
+def test_errors(scenario):
+    assert scenario.wtf.validate()
+    assert not scenario.wtf_formdata.validate()
+    assert scenario.pydantic.validate()
+    assert not scenario.pydantic_formdata.validate()
+
+    assert scenario.wtf.errors == {}
+    assert scenario.wtf_formdata.errors
+    assert scenario.pydantic.errors == {}
+    assert scenario.pydantic_formdata.errors