From 79827fc0b0feecae8b773ae733c3cb4147a9dfa9 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 1 Feb 2024 14:13:05 +0100 Subject: [PATCH 1/3] fix(float_serialization): float number were serialized as int --- .../agent_toolkit/services/serializers/json_api.py | 4 ++++ .../datasource_django/utils/record_serializer.py | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api.py b/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api.py index a40a3f645..a8379e6d7 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api.py @@ -28,6 +28,8 @@ class IntOrFloat(fields.Field): def _serialize(self, value, attr, obj, **kwargs): # type: ignore if value is None: return value + if isinstance(value, int) or isinstance(value, float): + return value try: return int(value) except ValueError: @@ -36,6 +38,8 @@ def _serialize(self, value, attr, obj, **kwargs): # type: ignore def _deserialize(self, value, attr, data, **kwargs): # type: ignore if value is None: return value + if isinstance(value, int) or isinstance(value, float): + return value try: return int(value) except ValueError: diff --git a/src/datasource_django/forestadmin/datasource_django/utils/record_serializer.py b/src/datasource_django/forestadmin/datasource_django/utils/record_serializer.py index 1f1c60359..8afceece3 100644 --- a/src/datasource_django/forestadmin/datasource_django/utils/record_serializer.py +++ b/src/datasource_django/forestadmin/datasource_django/utils/record_serializer.py @@ -1,3 +1,6 @@ +from decimal import Decimal +from typing import Any + from django.db.models import Model from forestadmin.datasource_toolkit.interfaces.query.projections import Projection from forestadmin.datasource_toolkit.interfaces.records import RecordsDataAlias @@ -6,7 +9,7 @@ def instance_to_record_data(instance: Model, projection: Projection) -> RecordsDataAlias: record_data = {} for field_name in projection.columns: - record_data[field_name] = getattr(instance, field_name) + record_data[field_name] = serialize_value(getattr(instance, field_name)) for relation_name, subfields in projection.relations.items(): relation = getattr(instance, relation_name, None) @@ -16,3 +19,9 @@ def instance_to_record_data(instance: Model, projection: Projection) -> RecordsD record_data[relation_name] = None return record_data + + +def serialize_value(value: Any): + if isinstance(value, Decimal): + return float(value) + return value From 3614ea75d6b8ab440e6c92ee677398e86ab65c7f Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 1 Feb 2024 14:27:49 +0100 Subject: [PATCH 2/3] chore: add tests for decimal serialization --- .../tests/test_django_collection.py | 14 ++++++++++++++ .../test_app/fixtures/book.json | 6 +++--- .../test_app/migrations/0001_initial.py | 16 +++++++++++----- .../test_project_datasource/test_app/models.py | 1 + 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/datasource_django/tests/test_django_collection.py b/src/datasource_django/tests/test_django_collection.py index 2ab313f7d..5d3d50741 100644 --- a/src/datasource_django/tests/test_django_collection.py +++ b/src/datasource_django/tests/test_django_collection.py @@ -140,6 +140,20 @@ async def test_list_should_work_with_null_relations(self): ], ) + async def test_decimal_should_be_correctly_serialized(self): + ret = await self.book_collection.list( + self.mocked_caller, + PaginatedFilter({"condition_tree": ConditionTreeLeaf("name", Operator.EQUAL, "Unknown Book")}), + Projection("id", "name", "price"), + ) + + self.assertEqual( + ret, + [ + {"id": 3, "name": "Unknown Book", "price": 3.45}, + ], + ) + class TestDjangoCollectionCRUDAggregateBase(TestCase): fixtures = ["person.json", "book.json", "rating.json"] diff --git a/src/datasource_django/tests/test_project_datasource/test_app/fixtures/book.json b/src/datasource_django/tests/test_project_datasource/test_app/fixtures/book.json index ef9f2e782..1c3068213 100644 --- a/src/datasource_django/tests/test_project_datasource/test_app/fixtures/book.json +++ b/src/datasource_django/tests/test_project_datasource/test_app/fixtures/book.json @@ -2,16 +2,16 @@ { "model": "test_app.book", "pk": 1, - "fields": { "name": "Foundation", "author": 1 } + "fields": { "name": "Foundation", "author": 1 , "price": 1.23} }, { "model": "test_app.book", "pk": 2, - "fields": { "name": "Harry Potter", "author": 2 } + "fields": { "name": "Harry Potter", "author": 2 , "price": 2.34} }, { "model": "test_app.book", "pk": 3, - "fields": { "name": "Unknown Book", "author": null } + "fields": { "name": "Unknown Book", "author": null , "price": 3.45} } ] diff --git a/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py b/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py index 7d80c015c..0ecf2db37 100644 --- a/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py +++ b/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py @@ -1,16 +1,17 @@ -# Generated by Django 4.2.7 on 2023-12-20 13:07 +# Generated by Django 4.2.8 on 2024-02-01 13:19 import django.contrib.auth.models -import django.db.models.deletion from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): + initial = True dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), ("auth", "0012_alter_user_first_name_max_length"), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ @@ -27,6 +28,7 @@ class Migration(migrations.Migration): ), ), ("name", models.CharField(max_length=254)), + ("price", models.DecimalField(decimal_places=2, max_digits=5)), ], ), migrations.CreateModel( @@ -74,13 +76,17 @@ class Migration(migrations.Migration): ("comment", models.TextField(null=True)), ( "rating", - models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]), + models.IntegerField( + choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] + ), ), ("rated_at", models.DateField()), ("object_id", models.PositiveIntegerField(null=True)), ( "book", - models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="test_app.book"), + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="test_app.book" + ), ), ( "commenter", diff --git a/src/datasource_django/tests/test_project_datasource/test_app/models.py b/src/datasource_django/tests/test_project_datasource/test_app/models.py index 4df0c3f41..ad9dcdc27 100644 --- a/src/datasource_django/tests/test_project_datasource/test_app/models.py +++ b/src/datasource_django/tests/test_project_datasource/test_app/models.py @@ -12,6 +12,7 @@ class Meta: class Book(models.Model): name = models.CharField(max_length=254) author = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="books", null=True) + price = models.DecimalField(decimal_places=2, max_digits=5) class Person(models.Model): From 1bc625b53bbefcf5c96ce9b7ad8ff694d7fd8384 Mon Sep 17 00:00:00 2001 From: Julien Barreau Date: Thu, 1 Feb 2024 15:16:58 +0100 Subject: [PATCH 3/3] chore: fix black and isort for CI --- .../test_app/migrations/0001_initial.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py b/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py index 0ecf2db37..971d7980e 100644 --- a/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py +++ b/src/datasource_django/tests/test_project_datasource/test_app/migrations/0001_initial.py @@ -1,8 +1,8 @@ # Generated by Django 4.2.8 on 2024-02-01 13:19 import django.contrib.auth.models -from django.db import migrations, models import django.db.models.deletion +from django.db import migrations, models class Migration(migrations.Migration): @@ -76,17 +76,13 @@ class Migration(migrations.Migration): ("comment", models.TextField(null=True)), ( "rating", - models.IntegerField( - choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] - ), + models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]), ), ("rated_at", models.DateField()), ("object_id", models.PositiveIntegerField(null=True)), ( "book", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to="test_app.book" - ), + models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="test_app.book"), ), ( "commenter",