] = ret = obj.__get__(obj, cls)
setattr(cls, name, ret)
else:
- if obj._is_dataclass:
- ret = obj.fget()
- else:
-
+ if is_dataclass:
# access attribute using normal class access
+ # first, to see if it's been mapped on a
+ # superclass. note if the dataclasses.field()
+ # has "default", this value can be anything.
+ ret = getattr(cls, name, None)
+
+ # so, if it's anything that's not ORM
+ # mapped, assume we should invoke the
+ # declared_attr
+ if not isinstance(ret, InspectionAttr):
+ ret = obj.fget()
+ else:
+ # access attribute using normal class access.
+ # if the declared attr already took place
+ # on a superclass that is mapped, then
+ # this is no longer a declared_attr, it will
+ # be the InstrumentedAttribute
ret = getattr(cls, name)
# correct for proxies created from hybrid_property
decl_api = util.preloaded.orm_decl_api
obj = field_metadata[sa_dataclass_metadata_key]
if callable(obj) and not isinstance(obj, decl_api.declared_attr):
- return decl_api.declared_attr(obj, _is_dataclass=True)
- elif isinstance(obj, decl_api.declared_attr):
- obj._is_dataclass = True
+ return decl_api.declared_attr(obj)
+ else:
return obj
- return obj
class _DeferredMapperConfig(_ClassScanMapperConfig):
class DataclassesTest(fixtures.MappedTest, testing.AssertsCompiledSQL):
__requires__ = ("dataclasses",)
- run_setup_classes = "each"
- run_setup_mappers = "each"
-
@classmethod
def define_tables(cls, metadata):
Table(
class FieldEmbeddedMixinWLambdaTest(fixtures.DeclarativeMappedTest):
__requires__ = ("dataclasses",)
- run_setup_classes = "each"
- run_setup_mappers = "each"
-
@classmethod
def setup_classes(cls):
declarative = cls.DeclarativeBasic.registry.mapped
},
)
+ has_a_default: str = dataclasses.field(
+ default="some default",
+ metadata={"sa": lambda: Column(String(50))},
+ )
+
@declarative
@dataclasses.dataclass
class Widget(WidgetDC):
default=None,
metadata={"sa": Column(String(30), nullable=False)},
)
+
__mapper_args__ = dict(
polymorphic_on="type",
polymorphic_identity="normal",
)
+ @declarative
+ @dataclasses.dataclass
+ class SpecialWidget(Widget):
+ __tablename__ = "special_widgets"
+ __sa_dataclass_metadata_key__ = "sa"
+
+ special_widget_id: int = dataclasses.field(
+ init=False,
+ metadata={
+ "sa": Column(
+ ForeignKey("widgets.widget_id"), primary_key=True
+ )
+ },
+ )
+
+ magic: bool = dataclasses.field(
+ default=False, metadata={"sa": Column(Boolean)}
+ )
+
+ __mapper_args__ = dict(
+ polymorphic_identity="special",
+ )
+
@dataclasses.dataclass
class AccountDC:
cls.classes["Account"] = Account
cls.classes["Widget"] = Widget
cls.classes["User"] = User
+ cls.classes["SpecialWidget"] = SpecialWidget
def test_setup(self):
- Account, Widget, User = self.classes("Account", "Widget", "User")
+ Account, Widget, User, SpecialWidget = self.classes(
+ "Account", "Widget", "User", "SpecialWidget"
+ )
assert "account_id" in Widget.__table__.c
assert list(Widget.__table__.c.account_id.foreign_keys)[0].references(
)
assert inspect(Account).relationships.widgets.mapper is inspect(Widget)
+ assert "account_id" not in SpecialWidget.__table__.c
+
+ assert "has_a_default" in Widget.__table__.c
+ assert "has_a_default" not in SpecialWidget.__table__.c
+
assert "account_id" in User.__table__.c
assert list(User.__table__.c.account_id.foreign_keys)[0].references(
Account.__table__
)
+ def test_asdict_and_astuple_special_widget(self):
+ SpecialWidget = self.classes.SpecialWidget
+ widget = SpecialWidget(magic=True)
+ eq_(
+ dataclasses.asdict(widget),
+ {
+ "widget_id": None,
+ "account_id": None,
+ "has_a_default": "some default",
+ "name": None,
+ "special_widget_id": None,
+ "magic": True,
+ },
+ )
+ eq_(
+ dataclasses.astuple(widget),
+ (None, None, "some default", None, None, True),
+ )
+
class FieldEmbeddedMixinWDeclaredAttrTest(FieldEmbeddedMixinWLambdaTest):
__requires__ = ("dataclasses",)
},
)
+ has_a_default: str = dataclasses.field(
+ default="some default",
+ metadata={"sa": declared_attr(lambda: Column(String(50)))},
+ )
+
@declarative
@dataclasses.dataclass
class Widget(WidgetDC):
polymorphic_identity="normal",
)
+ @declarative
+ @dataclasses.dataclass
+ class SpecialWidget(Widget):
+ __tablename__ = "special_widgets"
+ __sa_dataclass_metadata_key__ = "sa"
+
+ special_widget_id: int = dataclasses.field(
+ init=False,
+ metadata={
+ "sa": Column(
+ ForeignKey("widgets.widget_id"), primary_key=True
+ )
+ },
+ )
+
+ magic: bool = dataclasses.field(
+ default=False, metadata={"sa": Column(Boolean)}
+ )
+
+ __mapper_args__ = dict(
+ polymorphic_identity="special",
+ )
+
@dataclasses.dataclass
class AccountDC:
cls.classes["Account"] = Account
cls.classes["Widget"] = Widget
cls.classes["User"] = User
+ cls.classes["SpecialWidget"] = SpecialWidget
class PropagationFromMixinTest(fixtures.TestBase):
__requires__ = ("dataclasses",)
- run_setup_classes = "each"
- run_setup_mappers = "each"
-
def test_propagate_w_plain_mixin_col(self, run_test):
@dataclasses.dataclass
class CommonMixin:
class PropagationFromAbstractTest(fixtures.TestBase):
__requires__ = ("dataclasses",)
- run_setup_classes = "each"
- run_setup_mappers = "each"
-
def test_propagate_w_plain_mixin_col(self, run_test):
@dataclasses.dataclass
class BaseType: