【问题标题】:SqlAlchemy dataclass behavior and declarative mixins: AttributeError: can't set attributeSqlAlchemy 数据类行为和声明性混合:AttributeError:无法设置属性
【发布时间】:2022-01-06 19:36:08
【问题描述】:

我有一组作为纯 python 数据类起草的类。这是一个精简的示例,其行为与我预期的一样:

from dataclasses import dataclass
from abc import ABC

@dataclass
class Owner:
    name: str = ""

@dataclass
class OwnedThing(ABC):
    owner: Owner = None

@dataclass
class Thing1(OwnedThing):
    name: str = ""


owner = Owner(name="foo")
thing = Thing1( name="bar", owner=owner)
print(thing.owner.name)

这里我们有一个基类,它确定许多派生类都将具有一个“所有者”对象。分配所有者很简单。

当我尝试添加 SqlAlchemy 以实现持久性时,我想保留一般继承结构,这样我就不必为所有各种拥有的事物重复这种所有者-事物关系。但是,当我这样做时,我不能再将所有者类实例分配给 .owner 属性:

from dataclasses import dataclass,field
from abc import ABC
from sqlalchemy.orm import declarative_mixin, declared_attr, relationship
from sqlalchemy import Column, ForeignKey

@dataclass
class Owner:
    name: str = ""

@declarative_mixin
@dataclass
class OwnedThing(ABC):
    owner_id: int = field(init=False, default=None)
    owner: Owner = None

    @declared_attr
    def owner_id(cls):
        return Column('owner_id', ForeignKey('owner.id'))

    @declared_attr
    def owner(cls):
        return relationship(Owner)

@dataclass
class Thing1(OwnedThing):
    name: str = ""


owner = Owner(name="foo")
thing = Thing1( name="bar", owner=owner)
print(thing.owner.name)

这将导致此堆栈跟踪:

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py:658: SAWarning: Unmanaged access of declarative attribute owner_id from non-mapped class OwnedThing
  default = getattr(cls, a_name, MISSING)
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py:658: SAWarning: Unmanaged access of declarative attribute owner from non-mapped class OwnedThing
  default = getattr(cls, a_name, MISSING)
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py:842: SAWarning: Unmanaged access of declarative attribute owner_id from non-mapped class OwnedThing
  if isinstance(getattr(cls, f.name, None), Field):
/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/dataclasses.py:842: SAWarning: Unmanaged access of declarative attribute owner from non-mapped class OwnedThing
  if isinstance(getattr(cls, f.name, None), Field):
Traceback (most recent call last):
  File "test_case_sql.py", line 30, in <module>
    thing = Thing1( name="bar", owner=owner)
  File "<string>", line 2, in __init__
AttributeError: can't set attribute

请注意,当我跳过基类时,事情会按我的预期工作,没有错误:

from dataclasses import dataclass,field
from abc import ABC
from sqlalchemy.orm import declarative_mixin, declared_attr, relationship
from sqlalchemy import Column, ForeignKey,Integer

@dataclass
class Owner:
    name: str = ""


@dataclass
class Thing1:
    owner_id: int = Column(Integer)
    owner: Owner = relationship("Owner")
    name: str = ""


owner = Owner(name="foo")
thing = Thing1( name="bar", owner=owner)
print(thing.owner.name)

我对 SqlAlchemy 的内部结构了解得不够多,无法理解我做错了什么,而且我之前也没有看到其他人遇到过这个问题。有人可以帮我继续使用数据类和混合吗?

【问题讨论】:

    标签: python sqlalchemy python-dataclasses


    【解决方案1】:

    事实证明,在类周围添加映射代码可以解决问题:

    from dataclasses import dataclass,field
    from abc import ABC
    from sqlalchemy.orm import declarative_mixin, declared_attr, relationship, registry
    from sqlalchemy import Column, ForeignKey, Integer
    
    mapper_registry = registry()
    
    @mapper_registry.mapped
    @dataclass
    class Owner:
        __tablename__ = "owner"
        id: int = Column(Integer, primary_key=True)
    
        name: str = ""
    
    @declarative_mixin
    @dataclass
    class OwnedThing(ABC):
        id: int = Column(Integer, primary_key=True)
        owner_id: int = field(init=False, default=None)
        owner: Owner = None
    
        @declared_attr
        def owner_id(cls):
            return Column('owner_id', ForeignKey('owner.id'))
    
        @declared_attr
        def owner(cls):
            return relationship(Owner)
    
    @mapper_registry.mapped
    @dataclass
    class Thing1(OwnedThing):
        __tablename__ = "thing"
    
        name: str = ""
    
    
    owner = Owner(name="foo")
    thing = Thing1( name="bar", owner=owner)
    print(thing.owner.name)
    

    仍有一些警告,但代码现在按预期运行。

    【讨论】:

      猜你喜欢
      • 2011-10-04
      • 2021-06-14
      • 2017-07-17
      • 2015-02-08
      • 2014-08-09
      • 1970-01-01
      • 2021-06-13
      相关资源
      最近更新 更多