litestar-org / polyfactory

Simple and powerful factories for mock data generation
https://polyfactory.litestar.dev/
MIT License
988 stars 78 forks source link

Bug: sqlalchemy factory `column_property` in `get_model_fields` not working #528

Closed wangxin688 closed 3 months ago

wangxin688 commented 4 months ago

Description

when column_property was added to sqlalchemy model, get_model_fields is not working as expected.

URL to code causing the issue

No response

MCVE

# Your MCVE code here
class Provider(Base):
    __tablename__ == "provider"
    id: Mapped[int] = mapped_column(primarykey=True)
    name: Mapped[str]
    circuits: Mapped[list["Circuit"]] = relationship(backpopulates="provider")

class Circuit(Base):
    __tablename__ == "circuit"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    provider_id: Mapped[int] = mapped_column(ForeignKey("provider.id", ondelete="CASCADE"))
    provider: Mapped["Provider"] = relationship(backpopulates="circuit")

Provider.circuit_count = column_property(
   select(func.count(Circuit.id))
    .where(
        Circuit.provider_id == Provider.id,
    )
    .scalar_subquery(),
    deferred=True,
)

from polyfactory.factories.pydantic_factory import ModelFactory
from polyfactory.factories.sqlalchemy_factory import SQLAlchemyFactory

from src.app.architecture.circuit.models import Provider, CircuitType

class ProviderFactory(SQLAlchemyFactory[Provider]):
    ...

class ProviderFactoryWithRs(SQLAlchemyFactory[Provider]):
    __set_relationships__ = True

class CircuitTypeFactory(SQLAlchemyFactory[CircuitType]):
    ...

class CircuitTypeFactoryWithRs(SQLAlchemyFactory[CircuitType]):
    __set_relationships__ = True

def test_sqla_provider_without_rs() -> None:
    provider = ProviderFactory.build()
    assert provider.buildout_circuit_purchase_frame == []
    assert provider.buildout_circuit_purchase_provider == []
    assert provider.circuit == []

Steps to reproduce

when `build()` method was called.
exceptions as follow:

Neither 'Label' object nor 'Comparator' object has an attribute 'nullable'
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 1494, in __getattr__
    return getattr(self.comparator, key)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Comparator' object has no attribute 'nullable'

The above exception was the direct cause of the following exception:

  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/sqlalchemy/sql/elements.py", line 1496, in __getattr__
    raise AttributeError(
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/polyfactory/factories/sqlalchemy_factory.py", line 135, in get_type_from_column
    if column.nullable:
       ^^^^^^^^^^^^^^^
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/polyfactory/factories/sqlalchemy_factory.py", line 147, in <genexpr>
    annotation=cls.get_type_from_column(column),
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/polyfactory/factories/sqlalchemy_factory.py", line 145, in get_model_fields
    fields_meta.extend(
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/polyfactory/factories/base.py", line 931, in process_kwargs
    for field_meta in cls.get_model_fields():
                      ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/.venv/lib/python3.11/site-packages/polyfactory/factories/base.py", line 1031, in build
    return cast("T", cls.__model__(**cls.process_kwargs(**kwargs)))
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/tests/architecture/test_provider.py", line 19, in test_sqla_provider_without_rs
    provider = ProviderFactory.build()
               ^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/bytedance/Documents/Codebase/bitsnaas/bitsnaas-server/tests/architecture/test_provider.py", line 38, in <module>
    test_sqla_provider_without_rs()
  File "/Users/bytedance/.pyenv/versions/3.11.7/lib/python3.11/runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "/Users/bytedance/.pyenv/versions/3.11.7/lib/python3.11/runpy.py", line 198, in _run_module_as_main (Current frame)
    return _run_code(code, main_globals, None,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: Neither 'Label' object nor 'Comparator' object has an attribute 'nullable'

Screenshots

"In the format of: ![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"

Logs

No response

Release Version

latest

Platform


[!NOTE]
While we are open for sponsoring on GitHub Sponsors and OpenCollective, we also utilize Polar.sh to engage in pledge-based sponsorship.

Check out all issues funded or available for funding on our Polar.sh dashboard

  • If you would like to see an issue prioritized, make a pledge towards it!
  • We receive the pledge once the issue is completed & verified
  • This, along with engagement in the community, helps us know which features are a priority to our users.

Fund with Polar

wangxin688 commented 4 months ago

seems it's a sqlalchemy issues. nullable is missing in column_property https://github.com/sqlalchemy/sqlalchemy/issues/10351

adhtruong commented 4 months ago

This looks like the same issues as https://github.com/litestar-org/polyfactory/issues/508. This is currently fixed in main but not released yet

adhtruong commented 3 months ago

Resolved in last release