diff --git a/django_pydantic_field/v2/forms.py b/django_pydantic_field/v2/forms.py index b2c49d2..510682c 100644 --- a/django_pydantic_field/v2/forms.py +++ b/django_pydantic_field/v2/forms.py @@ -53,18 +53,22 @@ def to_python(self, value: ty.Any) -> ty.Any: return value if value in self.empty_values: return None - elif isinstance(value, JSONString): - return value + try: - converted = self.adapter.validate_json(value) + if not isinstance(value, (str, bytes)): + # The form data may contain python objects for some cases (e.g. using django-constance). + value = self.adapter.validate_python(value) + elif not isinstance(value, JSONString): + # Otherwise, try to parse incoming JSON according to the schema. + value = self.adapter.validate_json(value) except pydantic.ValidationError as exc: error_params = {"value": value, "title": exc.title, "detail": exc.json(), "errors": exc.errors()} raise ValidationError(self.error_messages["schema_error"], code="invalid", params=error_params) from exc - if isinstance(converted, str): - return JSONString(converted) + if isinstance(value, str): + value = JSONString(value) - return converted + return value def prepare_value(self, value): if isinstance(value, InvalidJSONInput): diff --git a/pyproject.toml b/pyproject.toml index b103cbf..22d42c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "django-pydantic-field" -version = "0.3.1" +version = "0.3.2-beta1" description = "Django JSONField with Pydantic models as a Schema" readme = "README.md" license = { file = "LICENSE" } diff --git a/tests/v2/test_forms.py b/tests/v2/test_forms.py index 4a1081e..88e9e6a 100644 --- a/tests/v2/test_forms.py +++ b/tests/v2/test_forms.py @@ -1,4 +1,5 @@ import typing as ty +from datetime import date import django import pytest @@ -16,11 +17,20 @@ class SampleForm(Form): field = forms.SchemaField(ty.ForwardRef("SampleSchema")) -def test_form_schema_field(): +@pytest.mark.parametrize( + "raw_data, clean_data", + [ + ('{"stub_str": "abc", "stub_list": ["1970-01-01"]}', {"stub_str": "abc", "stub_list": ["1970-01-01"]}), + (b'{"stub_str": "abc", "stub_list": ["1970-01-01"]}', {"stub_str": "abc", "stub_list": ["1970-01-01"]}), + ({"stub_str": "abc", "stub_list": ["1970-01-01"]}, {"stub_str": "abc", "stub_list": ["1970-01-01"]}), + (InnerSchema(stub_str="abc", stub_list=[date(1970, 1, 1)]), {"stub_str": "abc", "stub_list": ["1970-01-01"]}), + ], +) +def test_form_schema_field(raw_data, clean_data): field = forms.SchemaField(InnerSchema) - cleaned_data = field.clean('{"stub_str": "abc", "stub_list": ["1970-01-01"]}') - assert cleaned_data == InnerSchema.model_validate({"stub_str": "abc", "stub_list": ["1970-01-01"]}) + cleaned_data = field.clean(raw_data) + assert cleaned_data == InnerSchema.model_validate(clean_data) def test_empty_form_values(): @@ -87,14 +97,17 @@ def test_forwardref_model_formfield(): assert cleaned_data["annotated_field"] == SampleSchema(field=2) -@pytest.mark.parametrize("export_kwargs", [ - {"include": {"stub_str", "stub_int"}}, - {"exclude": {"stub_list"}}, - {"exclude_unset": True}, - {"exclude_defaults": True}, - {"exclude_none": True}, - {"by_alias": True}, -]) +@pytest.mark.parametrize( + "export_kwargs", + [ + {"include": {"stub_str", "stub_int"}}, + {"exclude": {"stub_list"}}, + {"exclude_unset": True}, + {"exclude_defaults": True}, + {"exclude_none": True}, + {"by_alias": True}, + ], +) def test_form_field_export_kwargs(export_kwargs): field = forms.SchemaField(InnerSchema, required=False, **export_kwargs) value = InnerSchema.model_validate({"stub_str": "abc", "stub_list": ["1970-01-01"]})