Describe the bug
The new default_factory validation introduced in
commit 95ad9fe
(related to #12168) breaks WriteOnlyMapped relationships that use default_factory=list
in Declarative Dataclass Mapping.
Optional link from https://docs.sqlalchemy.org which documents the behavior that is expected
No response
SQLAlchemy Version in Use
2.1.0b1
DBAPI (i.e. the database driver)
asyncpg
Database Vendor and Major Version
PostgreSQL 18
Python Version
3.14.2
Operating system
Linux
To Reproduce
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, WriteOnlyMapped, mapped_column, relationship
class Base(DeclarativeBase, MappedAsDataclass):
pass
class Parent(Base):
__tablename__ = "parent"
id: Mapped[int] = mapped_column(primary_key=True)
children: WriteOnlyMapped["Child"] = relationship(
back_populates="parent",
default_factory=list, # ← raises ArgumentError in 2.1
cascade="all, delete-orphan",
passive_deletes=True,
)
class Child(Base):
__tablename__ = "child"
id: Mapped[int] = mapped_column(primary_key=True)
parent_id: Mapped[int] = mapped_column(foreign_key="parent.id", ondelete="CASCADE")
parent: Mapped[Parent] = relationship(back_populates="children")
Error
Traceback (most recent call last):
File "/home/.../project/sqla_relationship_bug.py", line 8, in
class Parent(Base):
...<8 lines>...
)
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_api.py", line 867, in init_subclass
_ORMClassConfigurator._as_declarative(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
cls._sa_registry, cls, cls.dict
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 289, in as_declarative
return DeclarativeMapperConfig(registry, cls, dict)
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 1013, in init
self._extract_mappable_attributes()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 1590, in _extract_mappable_attributes
value.declarative_scan(
~~~~~~~~~~~~~~~~~~~~~~^
self,
^^^^^
...<7 lines>...
is_dataclass,
^^^^^^^^^^^^^
)
^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/relationships.py", line 1920, in declarative_scan
raise sa_exc.ArgumentError(
...<3 lines>...
)
sqlalchemy.exc.ArgumentError: For relationship Parent.children using dataclass options, default_factory must be exactly None
Additional context
Root Cause
The new validation in relationships.py (added in commit 95ad9fe) checks:
if (
self._attribute_options.dataclasses_default_factory
is not _NoArg.NO_ARG
and self._attribute_options.dataclasses_default_factory
is not self.collection_class # ← None for WriteOnlyMapped
):
raise sa_exc.ArgumentError(...)
For WriteOnlyMapped, collection_class is None because Write-Only collections
define their collection type through the annotation itself, not through collection_class.
This causes default_factory=list to always fail the is not self.collection_class check.
The same pattern is already correctly handled just a few lines above for the uselist check:
if (
self.collection_class is None
and not is_write_only # ← already guards write-only here
and not is_dynamic
):
self.uselist = False
Proposed Fix
Add the same is_write_only and is_dynamic guards to the new validation:
if (
self._attribute_options.dataclasses_default_factory
is not _NoArg.NO_ARG
and self._attribute_options.dataclasses_default_factory
is not self.collection_class
and not is_write_only # ← add this
and not is_dynamic # ← add this
):
raise sa_exc.ArgumentError(...)
Describe the bug
The new
default_factoryvalidation introduced incommit 95ad9fe
(related to #12168) breaks
WriteOnlyMappedrelationships that usedefault_factory=listin Declarative Dataclass Mapping.
Optional link from https://docs.sqlalchemy.org which documents the behavior that is expected
No response
SQLAlchemy Version in Use
2.1.0b1
DBAPI (i.e. the database driver)
asyncpg
Database Vendor and Major Version
PostgreSQL 18
Python Version
3.14.2
Operating system
Linux
To Reproduce
Error
Traceback (most recent call last):
File "/home/.../project/sqla_relationship_bug.py", line 8, in
class Parent(Base):
...<8 lines>...
)
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_api.py", line 867, in init_subclass
_ORMClassConfigurator._as_declarative(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
cls._sa_registry, cls, cls.dict
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 289, in as_declarative
return DeclarativeMapperConfig(registry, cls, dict)
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 1013, in init
self._extract_mappable_attributes()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/decl_base.py", line 1590, in _extract_mappable_attributes
value.declarative_scan(
~~~~~~~~~~~~~~~~~~~~~~^
self,
^^^^^
...<7 lines>...
is_dataclass,
^^^^^^^^^^^^^
)
^
File "/home/.../project/.venv/lib/python3.14/site-packages/sqlalchemy/orm/relationships.py", line 1920, in declarative_scan
raise sa_exc.ArgumentError(
...<3 lines>...
)
sqlalchemy.exc.ArgumentError: For relationship Parent.children using dataclass options, default_factory must be exactly None
Additional context
Root Cause
The new validation in relationships.py (added in commit 95ad9fe) checks:
For
WriteOnlyMapped,collection_classisNonebecause Write-Only collectionsdefine their collection type through the annotation itself, not through
collection_class.This causes
default_factory=listto always fail theis not self.collection_classcheck.The same pattern is already correctly handled just a few lines above for the
uselistcheck:Proposed Fix
Add the same
is_write_onlyandis_dynamicguards to the new validation: