From 01648bd991499987fec7db4bd13c6a1ef845626b Mon Sep 17 00:00:00 2001 From: Tyson Cung Date: Sat, 28 Mar 2026 15:01:18 +0000 Subject: [PATCH] fix(sqlalchemy): skip system columns during persistence (#802) Columns with system=True (e.g., PostgreSQL system columns) should be skipped during persistence, similar to how computed columns are handled. Values can still be generated normally during build(), but are excluded when using create_sync/create_async. Changes: - Added 'system' to SQLAlchemyConstraints TypedDict - Added 'skip_system_fields' to SQLAlchemyBuildContext - Detect system=True on columns in get_type_from_column - Skip system fields in should_set_field_value during persistence - Set skip_system_fields=True in create_sync/create_async Closes #802 --- polyfactory/factories/sqlalchemy_factory.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/polyfactory/factories/sqlalchemy_factory.py b/polyfactory/factories/sqlalchemy_factory.py index 9521ced1..322d4999 100644 --- a/polyfactory/factories/sqlalchemy_factory.py +++ b/polyfactory/factories/sqlalchemy_factory.py @@ -48,10 +48,12 @@ class SQLAlchemyBuildContext(BaseBuildContext): skip_computed_fields: bool + skip_system_fields: bool class SQLAlchemyConstraints(Constraints): computed: NotRequired[bool] + system: NotRequired[bool] class SQLAlchemyPersistenceMethod(enum.Enum): @@ -161,6 +163,8 @@ def _get_build_context( build_context = cast("SQLAlchemyBuildContext", super()._get_build_context(build_context)) if build_context.get("skip_computed_fields") is None: build_context["skip_computed_fields"] = False + if build_context.get("skip_system_fields") is None: + build_context["skip_system_fields"] = False return build_context @@ -168,6 +172,7 @@ def _get_build_context( def create_sync(cls, **kwargs: Any) -> T: build_context = cls._get_build_context(kwargs.get("_build_context")) build_context["skip_computed_fields"] = True + build_context["skip_system_fields"] = True kwargs["_build_context"] = build_context return super().create_sync(**kwargs) @@ -175,6 +180,7 @@ def create_sync(cls, **kwargs: Any) -> T: async def create_async(cls, **kwargs: Any) -> T: build_context = cls._get_build_context(kwargs.get("_build_context")) build_context["skip_computed_fields"] = True + build_context["skip_system_fields"] = True kwargs["_build_context"] = build_context return await super().create_async(**kwargs) @@ -240,6 +246,8 @@ def should_set_field_value(cls, field_meta: FieldMeta, **kwargs: Any) -> bool: constraints = cast("SQLAlchemyConstraints", field_meta.constraints) if constraints.get("computed") and build_context.get("skip_computed_fields"): return False + if constraints.get("system") and build_context.get("skip_system_fields"): + return False return super().should_set_field_value(field_meta, **kwargs) @@ -309,6 +317,10 @@ def get_type_from_column(cls, column: Column) -> type: constraints: SQLAlchemyConstraints = {"computed": True} annotation = Annotated[annotation, Frozendict(constraints)] # type: ignore[assignment] + if getattr(column, "system", False): + system_constraints: SQLAlchemyConstraints = {"system": True} + annotation = Annotated[annotation, Frozendict(system_constraints)] # type: ignore[assignment] + return annotation @classmethod