From 17a90f74f0ee1fb1bd0e11d18f045796146e3bfb Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 9 Jan 2024 17:27:51 +0400 Subject: [PATCH 1/2] value_from_object --- django_pydantic_field/v1/fields.py | 3 +++ django_pydantic_field/v2/fields.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/django_pydantic_field/v1/fields.py b/django_pydantic_field/v1/fields.py index 188ecf4..b1caee8 100644 --- a/django_pydantic_field/v1/fields.py +++ b/django_pydantic_field/v1/fields.py @@ -125,6 +125,9 @@ def formfield(self, **kwargs): field_kwargs.update(kwargs) return super().formfield(**field_kwargs) + def value_from_object(self, obj): + return super().value_from_object().dict() + def _resolve_schema(self, schema): schema = t.cast(t.Type["base.ST"], GenericContainer.unwrap(schema)) diff --git a/django_pydantic_field/v2/fields.py b/django_pydantic_field/v2/fields.py index d8e6a8e..7d0f771 100644 --- a/django_pydantic_field/v2/fields.py +++ b/django_pydantic_field/v2/fields.py @@ -187,6 +187,9 @@ def formfield(self, **kwargs): field_kwargs.update(kwargs) return super().formfield(**field_kwargs) # type: ignore + def value_from_object(self, obj): + return super().value_from_object().model_dump() + class SchemaKeyTransformAdapter: """An adapter for creating key transforms for schema field lookups.""" From 0a4c5e0647707000a6b668c9e9b576d9f5a56fd0 Mon Sep 17 00:00:00 2001 From: Savva Surenkov Date: Wed, 10 Jan 2024 00:12:55 +0400 Subject: [PATCH 2/2] Replace `value_from_object` with `value_to_string` - Add tests for django.core.serializers --- django_pydantic_field/v1/fields.py | 7 ++++--- django_pydantic_field/v2/fields.py | 11 +++++++++-- tests/test_e2e_models.py | 27 +++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/django_pydantic_field/v1/fields.py b/django_pydantic_field/v1/fields.py index b1caee8..e0baa59 100644 --- a/django_pydantic_field/v1/fields.py +++ b/django_pydantic_field/v1/fields.py @@ -9,7 +9,7 @@ from django.db.models.fields.json import JSONField from django.db.models.query_utils import DeferredAttribute -from django_pydantic_field.compat.django import GenericContainer, GenericTypes +from django_pydantic_field.compat.django import GenericContainer from . import base, forms, utils @@ -125,8 +125,9 @@ def formfield(self, **kwargs): field_kwargs.update(kwargs) return super().formfield(**field_kwargs) - def value_from_object(self, obj): - return super().value_from_object().dict() + def value_to_string(self, obj): + value = self.value_from_object(obj) + return self.get_prep_value(value) def _resolve_schema(self, schema): schema = t.cast(t.Type["base.ST"], GenericContainer.unwrap(schema)) diff --git a/django_pydantic_field/v2/fields.py b/django_pydantic_field/v2/fields.py index 7d0f771..2a53db8 100644 --- a/django_pydantic_field/v2/fields.py +++ b/django_pydantic_field/v2/fields.py @@ -19,6 +19,7 @@ if ty.TYPE_CHECKING: import json import typing_extensions as te + from django.db.models import Model class _SchemaFieldKwargs(types.ExportKwargs, total=False): # django.db.models.fields.Field kwargs @@ -148,6 +149,11 @@ def validate(self, value: ty.Any, model_instance: ty.Any) -> None: return super(JSONField, self).validate(value, model_instance) def to_python(self, value: ty.Any): + try: + value = self.adapter.validate_json(value) + except ValueError: + """This is an expected error, this step is required to parse serialized values.""" + try: return self.adapter.validate_python(value) except pydantic.ValidationError as exc: @@ -187,8 +193,9 @@ def formfield(self, **kwargs): field_kwargs.update(kwargs) return super().formfield(**field_kwargs) # type: ignore - def value_from_object(self, obj): - return super().value_from_object().model_dump() + def value_to_string(self, obj: Model): + value = super().value_from_object(obj) + return self.get_prep_value(value) class SchemaKeyTransformAdapter: diff --git a/tests/test_e2e_models.py b/tests/test_e2e_models.py index c48cf73..e191b7e 100644 --- a/tests/test_e2e_models.py +++ b/tests/test_e2e_models.py @@ -1,6 +1,7 @@ from datetime import date import pytest +from django.core import serializers from django.db.models import F, Q, JSONField, Value from tests.conftest import InnerSchema @@ -46,6 +47,32 @@ def test_model_db_serde(initial_payload, expected_values): assert instance_values == expected_values +@pytest.mark.parametrize("format", ["python", "json", "yaml", "jsonl"]) +@pytest.mark.parametrize( + "payload", + [ + { + "sample_field": InnerSchema(stub_str="abc", stub_list=[date(2023, 6, 1)]), + "sample_list": [InnerSchema(stub_str="abc", stub_list=[])], + }, + { + "sample_field": {"stub_str": "abc", "stub_list": ["2023-06-01"]}, + "sample_list": [{"stub_str": "abc", "stub_list": []}], + }, + ], +) +def test_model_serialization(payload, format): + instance = SampleModel(**payload) + instance_values = {k: getattr(instance, k) for k in payload.keys()} + + serialized_instances = serializers.serialize(format, [instance]) + deserialized_instance = next(serializers.deserialize(format, serialized_instances)).object + deserialized_values = {k: getattr(deserialized_instance, k) for k in payload.keys()} + + assert instance_values == deserialized_values + assert serialized_instances == serializers.serialize(format, [deserialized_instance]) + + @pytest.mark.parametrize( "lookup", [