Skip to content

generic_relationship breaks with SqlAlchemy 2.0.22 #719

@holyoaks

Description

@holyoaks

I have two table classes defined using SqlAlchemy 2.0 syntax. Yesterday, using SqlAlchemy 2.0.21, my application worked fine. This morning, using SqlAlchemy 2.0.22, my application will not start. So why am I creating an issue here instead of SqlAlchemy?

Of the two table classes, one has a generic_relationship defined, while the other does not. Stepping through the code as the application attempts to start, the table class without a generic_relationship is mapped correctly, no issues. The second table class, with a generic_relationship, raises:

TypeError: AttributeImpl.__init__() missing 1 required positional argument: 'dispatch'

The two table classes, along with the definition of the Base declarative class, are shown below:

from sqlalchemy import Boolean, DateTime, ForeignKey
from sqlalchemy.dialects.postgresql import BIGINT, JSONB
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import DeclarativeBase, mapped_column, relationship
from sqlalchemy.sql import func
from sqlalchemy.sql.schema import Index
from sqlalchemy.sql.sqltypes import Integer, String, Unicode
from sqlalchemy_utils import generic_relationship


class Base(DeclarativeBase):
    pass


class EventCategory(Base):
    __tablename__ = "eventcategory"
    id = mapped_column(BIGINT, primary_key=True, index=True)
    icon_url = mapped_column(
        String, nullable=True
    )
    is_archived = mapped_column(Boolean, default=False)
    label = mapped_column(String)

    events = relationship("Event", back_populates="category")


class Event(Base):
    __tablename__ = "event"
    id = mapped_column(BIGINT, primary_key=True, index=True)
    action = mapped_column(String)
    category_id = mapped_column(BIGINT, ForeignKey("eventcategory.id", ondelete="CASCADE"))
    datetime_occurred = mapped_column(DateTime(timezone=True), default=func.now())
    is_internal = mapped_column(Boolean, default=False)
    object_id = mapped_column(Integer, nullable=True)
    object_data = mapped_column(MutableDict.as_mutable(JSONB), default={})
    object_type = mapped_column(Unicode)
    permalink = mapped_column(String, nullable=True)
    source_user_name = mapped_column(String)
    text_map_values = mapped_column(MutableDict.as_mutable(JSONB), default={})

    category = relationship("EventCategory", back_populates="events")
    object = generic_relationship(object_type, object_id)

    __table_args__ = (
        Index("ix_event_object_data", "object_data", postgresql_using="gin"),
        Index("ix_object_event", "object_id", "object_type"),
    )

Full traceback:

    class Event(Base):
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_api.py", line 847, in __init_subclass__
    _as_declarative(cls._sa_registry, cls, cls.__dict__)
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_base.py", line 247, in _as_declarative
    return _MapperConfig.setup_mapping(registry, cls, dict_, None, {})
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_base.py", line 328, in setup_mapping
    return _ClassScanMapperConfig(
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_base.py", line 582, in __init__
    self._early_mapping(mapper_kw)
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_base.py", line 369, in _early_mapping
    self.map(mapper_kw)
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/decl_base.py", line 1949, in map
    mapper_cls(self.cls, self.local_table, **self.mapper_args),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<string>", line 2, in __init__
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/util/deprecations.py", line 281, in warned
    return fn(*args, **kwargs)  # type: ignore[no-any-return]
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py", line 851, in __init__
    self._configure_properties()
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py", line 1748, in _configure_properties
    self._configure_property(
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py", line 2230, in _configure_property
    prop.instrument_class(self)
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy_utils/generic.py", line 173, in instrument_class
    attributes.register_attribute(
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 2588, in register_attribute
    register_attribute_impl(class_, key, **kw)
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 2620, in register_attribute_impl
    impl = cast("Type[WriteOnlyAttributeImpl]", impl_class)(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/attributes.py", line 1217, in __init__
    super().__init__(*arg, **kw)
TypeError: AttributeImpl.__init__() missing 1 required positional argument: 'dispatch'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions