-
-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(sqlalchemy-factory): Add support for SQLAlchemy custom types #398
feat(sqlalchemy-factory): Add support for SQLAlchemy custom types #398
Conversation
…corator, UserDefinedType)
…` override tests
Documentation preview will be available shortly at https://litestar-org.github.io/polyfactory-docs-preview/398 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! I just have a few comments/doubts :)
elif issubclass(column_type, types.UserDefinedType): | ||
parameter_exc_msg = ( | ||
f"User defined type detected (subclass of {types.UserDefinedType}). " | ||
"Override get_sqlalchemy_types to provide factory function." | ||
) | ||
raise ParameterException(parameter_exc_msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to throw the exception here itself. If the provider map doesn't have the corresponding factory function, then the exception will be thrown from the get_field_value
method. Instead, this can just return the column type directly if the column type is a subclass off UserDefinedType
.
@@ -118,6 +118,17 @@ def get_type_from_column(cls, column: Column) -> type: | |||
annotation = column_type | |||
elif issubclass(column_type, types.ARRAY): | |||
annotation = List[column.type.item_type.python_type] # type: ignore[assignment,name-defined] | |||
elif issubclass(column_type, types.TypeDecorator) and isinstance( | |||
python_type := column_type.impl.python_type, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the test for python_type
being an instance of type
needed? Also, what happens if there is no python_type
implemented for the column_type.impl
? For example, it could be set to postgresql.CIDR
or types.ARRAY
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Essentially, I think the parsing of the annotation from the column type needs to be recursive in the case of types.TypeDecorator
.
UserDefinedType based | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
``get_sqlalchemy_types`` classmethod needs to be overridden to provide factory function for custom type. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be changed so that the user should override the get_provider_map
method. That makes it more consistent with how the other factories handle custom user defined types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This provides the mapping from sqlalchemy types to provider where this is directly mapped from column vs where the implementation type could be used.
I think these could be unified but may require some reworking so the the logic of handling types in exposed separately. There wasn't a clear way to map things like Integer
column without repeating int handling. This case is alright to handle but this becomes more complex especially when considering things like list[str]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This provides the mapping from sqlalchemy types to provider where this is directly mapped from column vs where the implementation type could be used.
I think these could be unified but may require some reworking so the the logic of handling types in exposed separately. There wasn't a clear way to map things like
Integer
column without repeating int handling. This case is alright to handle but this becomes more complex especially when considering things likelist[str]
.
I agree that we can keep a separate get_sqlalchemy_types
and a get_provider_map
so that we can deal with sql
types in an easier manner. However, since this is a user defined type, should it not go into the get_provider_map
which is where the user defined types are supposed to be given for the other factories?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not with the current implementation.
get_type_from_column
only considers the output of get_sqlalchemy_types
here for SQLA types it should not map for providers. This could be changed to just using get_provider_map
to bring inline with other factories.
I don't think this is a backwards breaking change though one very minor concern it may be harder where want to provide a mapping for a field with type of Column itself. This is minor as feels a fairly obscure use case.
@adhtruong If you have time, could you take a look at this as well? |
@@ -85,3 +87,95 @@ class ModelFactory(SQLAlchemyFactory[Model]): | |||
|
|||
instance = ModelFactory.build() | |||
assert instance.overridden is not None | |||
|
|||
|
|||
@pytest.mark.parametrize( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this applicable to SQLA 1.4? If so, I think these tests should be moved to _common and adjusted to common so tested against both 1.4 and 2
@bc291 any updates? |
Thanks for the work on this @bc291 . I think this use case is now covered by https://github.com/litestar-org/polyfactory/pull/513/files. Please reopen if missed off a use case here. You will need to sync with main |
Pull Request Checklist
Description
Currently custom SQLAlchemy types are not supported which results in:
This PR adds support for custom types created by:
sqlalchemy.types.TypeDecorator
; note:impl
type needs to be python-mappablesqlalchemy.types.UserDefinedType
:In this case,
ParameterException
is raised which encourages user to overrideget_sqlalchemy_types
class method in the factory:Close Issue(s)