Repository: litestar-org/polyfactory
Pull request #542 (2024-05-10 14:22:19)

fix(sqla_factory): fix json type error and pg dialect default value e…
## Description
Fix json type error in #530. Define JSON type for mysql, mssql, sqlite and postgresql dialect for each one.

one more things I made a fix:
for `CIDR` type postgresql: I believe it should be network rather than a IP address, so netmask should be generated for `CIDR` type ^^(I'm a network engineer with CCIE before).
for `INET` type: it can be a IP address without netmask or a ip_network address. 
So I change `CIDR` to `faker(network=True)` and `INET` to `faker(network=False)`


github-actions[bot]: Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/542

Patch:
diff --git a/polyfactory/factories/sqlalchemy_factory.py b/polyfactory/factories/sqlalchemy_factory.py
index f9897dd..d9b0ac6 100644
--- a/polyfactory/factories/sqlalchemy_factory.py
+++ b/polyfactory/factories/sqlalchemy_factory.py
@@ -10,7 +10,7 @@ from polyfactory.persistence import AsyncPersistenceProtocol, SyncPersistencePro
 
 try:
     from sqlalchemy import Column, inspect, types
-    from sqlalchemy.dialects import mysql, postgresql
+    from sqlalchemy.dialects import mssql, mysql, postgresql, sqlite
     from sqlalchemy.exc import NoInspectionAvailable
     from sqlalchemy.orm import InstanceState, Mapper
 except ImportError as e:
@@ -85,22 +85,28 @@ class SQLAlchemyFactory(Generic[T], BaseFactory[T]):
 
     @classmethod
     def get_sqlalchemy_types(cls) -> dict[Any, Callable[[], Any]]:
-        """Get mapping of types where column type."""
+        """Get mapping of types where column type.
+        for sqlalchemy dialect `JSON` type, accepted only basic types in pydict in case sqlalchemy process `JSON` raise serialize error.
+        """
         return {
             types.TupleType: cls.__faker__.pytuple,
+            mssql.JSON: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
             mysql.YEAR: lambda: cls.__random__.randint(1901, 2155),
-            postgresql.CIDR: lambda: cls.__faker__.ipv4(network=False),
+            mysql.JSON: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
+            postgresql.CIDR: lambda: cls.__faker__.ipv4(network=True),
             postgresql.DATERANGE: lambda: (cls.__faker__.past_date(), date.today()),  # noqa: DTZ011
-            postgresql.INET: lambda: cls.__faker__.ipv4(network=True),
+            postgresql.INET: lambda: cls.__faker__.ipv4(network=False),
             postgresql.INT4RANGE: lambda: tuple(sorted([cls.__faker__.pyint(), cls.__faker__.pyint()])),
             postgresql.INT8RANGE: lambda: tuple(sorted([cls.__faker__.pyint(), cls.__faker__.pyint()])),
             postgresql.MACADDR: lambda: cls.__faker__.hexify(text="^^:^^:^^:^^:^^:^^", upper=True),
             postgresql.NUMRANGE: lambda: tuple(sorted([cls.__faker__.pyint(), cls.__faker__.pyint()])),
             postgresql.TSRANGE: lambda: (cls.__faker__.past_datetime(), datetime.now()),  # noqa: DTZ005
             postgresql.TSTZRANGE: lambda: (cls.__faker__.past_datetime(), datetime.now()),  # noqa: DTZ005
-            postgresql.HSTORE: lambda: cls.__faker__.pydict(),
-            # `types.JSON` is compatible for sqlachemy extend dialects. Such as `pg.JSON` and `JSONB`
-            types.JSON: lambda: cls.__faker__.pydict(),
+            postgresql.HSTORE: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
+            postgresql.JSON: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
+            postgresql.JSONB: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
+            sqlite.JSON: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
+            types.JSON: lambda: cls.__faker__.pydict(value_types=(str, int, bool, float)),
         }
 
     @classmethod
@@ -148,7 +154,6 @@ class SQLAlchemyFactory(Generic[T], BaseFactory[T]):
 
         if column.nullable:
             annotation = Union[annotation, None]  # type: ignore[assignment]
-
         return annotation
 
     @classmethod


Test patch:
diff --git a/tests/sqlalchemy_factory/test_sqlalchemy_factory_v2.py b/tests/sqlalchemy_factory/test_sqlalchemy_factory_v2.py
index de2ab7e..b7d7d7d 100644
--- a/tests/sqlalchemy_factory/test_sqlalchemy_factory_v2.py
+++ b/tests/sqlalchemy_factory/test_sqlalchemy_factory_v2.py
@@ -71,7 +71,7 @@ def test_python_type_handling_v2() -> None:
     assert isinstance(instance.str_array_type[0], str)
 
 
-def test_pg_dialect_types() -> None:
+def test_sqla_dialect_types() -> None:
     class Base(orm.DeclarativeBase): ...
 
     class SqlaModel(Base):
@@ -115,11 +115,23 @@ def test_pg_dialect_types() -> None:
     assert isinstance(instance.mut_nested_arry_inet[0], str)
     assert ip_network(instance.mut_nested_arry_inet[0])
     assert isinstance(instance.pg_json_type, dict)
+    for value in instance.pg_json_type.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.pg_jsonb_type, dict)
+    for value in instance.pg_jsonb_type.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.common_json_type, dict)
+    for value in instance.common_json_type.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.mysql_json, dict)
+    for value in instance.mysql_json.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.sqlite_json, dict)
+    for value in instance.sqlite_json.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.mssql_json, dict)
+    for value in instance.mssql_json.values():
+        assert isinstance(value, (str, int, bool, float))
     assert isinstance(instance.multible_pg_json_type, dict)
     assert isinstance(instance.multible_pg_jsonb_type, dict)
     assert isinstance(instance.multible_common_json_type, dict)