如何在 sqlalchemy 经典映射上使用 cattrs 构造数据类?
How to structure dataclass with cattrs on sqlalchemy classical mapping?
我正在尝试在我使用 sqlalchemy 的应用程序中使用 cattr 来构造一些 classes。出于某种原因,在我向数据库提交 class 后,无法构造回任何嵌套数据 class。我收到错误:
in emit_backref_from_collection_append_event
child_state, child_dict = instance_state(child), instance_dict(child)
AttributeError: 'dict' object has no attribute '_sa_instance_state'
这是一个代码示例。
from dataclasses import dataclass
from sqlalchemy.orm import backref, relationship, registry, sessionmaker
from sqlalchemy import Table, Column, ForeignKey, create_engine
from sqlalchemy.sql.sqltypes import Integer, String
import cattr
from src.vlep.domain.model import Patient
mapper_registry = registry()
parents = Table(
'parents', mapper_registry.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', String(255)),
)
children = Table(
'children', mapper_registry.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', String(255)),
Column('parent_id', ForeignKey('parents.id', ondelete='SET NULL'))
)
def start_mappers():
children_mapper = mapper_registry.map_imperatively(
Child,
children,
)
parent_mapper = mapper_registry.map_imperatively(
Parent,
parents,
properties={
'children': relationship(
children_mapper,
backref=backref('parents'))
}
)
@dataclass
class Child:
name: str
@dataclass
class Parent:
name: str
children: list
if __name__ == "__main__":
engine = create_engine("sqlite:///:memory:")
mapper_registry.metadata.create_all(engine)
start_mappers()
session = sessionmaker(bind=engine)()
c1 = Child('Uguinho')
c2 = Child('Luizinho')
p = Parent('Fulano', [c1, c2])
session.add(p)
session.commit()
try:
# This works
d = cattr.unstructure(p, Child)
p2 = cattr.structure(d, Child)
d2 = cattr.unstructure(p2, Child)
print(d2)
finally:
# This does not
d = cattr.unstructure(p, Parent)
p2 = cattr.structure(d, Parent)
d2 = cattr.unstructure(p2, Parent)
print(d2)
根据错误消息,我猜测 sqlalchemy 正在尝试检查 parent_mapper
上的 属性 'children' 是否是 Child
的列表并且它正在获取dict
而不是(它处于非结构化状态)。我什至不知道 sqlalchemy 是如何进行这项检查的。再次猜测,我想它总是会做一些一致性检查。无论如何,我现在知道如何解决这个问题了。
所以我仔细研究了 SQLAlchemy 文档,发现并改编了 an example 让您的代码工作:
from dataclasses import dataclass
from typing import List
import cattr
from sqlalchemy.orm import (
relationship,
sessionmaker,
registry,
)
from sqlalchemy import Table, Column, ForeignKey, create_engine, MetaData
from sqlalchemy.sql.sqltypes import Integer, String
# commented this out for the example
# from src.vlep.domain.model import Patient
mapper_registry = registry()
@dataclass
class Child:
name: str
@dataclass
class Parent:
name: str
children: List[Child]
metadata_obj = MetaData()
parent = Table(
"parent",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("name", String(255)),
)
child = Table(
"child",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("name", String(255)),
Column("parent_id", ForeignKey("parent.id", ondelete="SET NULL")),
)
def start_mappers():
mapper_registry.map_imperatively(
Parent,
parent,
properties={"children": relationship(Child, backref="parent")},
)
mapper_registry.map_imperatively(Child, child)
if __name__ == "__main__":
engine = create_engine("sqlite:///:memory:")
metadata_obj.create_all(engine)
start_mappers()
session = sessionmaker(bind=engine)()
c1 = Child("Uguinho")
c2 = Child("Luizinho")
p = Parent("Fulano", [c1, c2])
session.add(p)
session.commit()
try:
# This works
d = cattr.unstructure(p, Child)
p2 = cattr.structure(d, Child)
d2 = cattr.unstructure(p2, Child)
print(d2)
finally:
# This now works
d = cattr.unstructure(p, Parent)
p2 = cattr.structure(d, Parent)
d2 = cattr.unstructure(p2, Parent)
print(d2)
输出:
{'name': 'Fulano'}
{'name': 'Fulano', 'children': [{'name': 'Uguinho'}, {'name': 'Luizinho'}]}
我认为可能主要问题之一是您将“backref”声明为 backref=backref('parents')
,而那里的函数包装器是不必要的?不太确定...
无论如何,数据类现在用于正确映射(据我所知)。我希望这个例子对你有用!
我正在尝试在我使用 sqlalchemy 的应用程序中使用 cattr 来构造一些 classes。出于某种原因,在我向数据库提交 class 后,无法构造回任何嵌套数据 class。我收到错误:
in emit_backref_from_collection_append_event
child_state, child_dict = instance_state(child), instance_dict(child)
AttributeError: 'dict' object has no attribute '_sa_instance_state'
这是一个代码示例。
from dataclasses import dataclass
from sqlalchemy.orm import backref, relationship, registry, sessionmaker
from sqlalchemy import Table, Column, ForeignKey, create_engine
from sqlalchemy.sql.sqltypes import Integer, String
import cattr
from src.vlep.domain.model import Patient
mapper_registry = registry()
parents = Table(
'parents', mapper_registry.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', String(255)),
)
children = Table(
'children', mapper_registry.metadata,
Column('id', Integer, primary_key=True, autoincrement=True),
Column('name', String(255)),
Column('parent_id', ForeignKey('parents.id', ondelete='SET NULL'))
)
def start_mappers():
children_mapper = mapper_registry.map_imperatively(
Child,
children,
)
parent_mapper = mapper_registry.map_imperatively(
Parent,
parents,
properties={
'children': relationship(
children_mapper,
backref=backref('parents'))
}
)
@dataclass
class Child:
name: str
@dataclass
class Parent:
name: str
children: list
if __name__ == "__main__":
engine = create_engine("sqlite:///:memory:")
mapper_registry.metadata.create_all(engine)
start_mappers()
session = sessionmaker(bind=engine)()
c1 = Child('Uguinho')
c2 = Child('Luizinho')
p = Parent('Fulano', [c1, c2])
session.add(p)
session.commit()
try:
# This works
d = cattr.unstructure(p, Child)
p2 = cattr.structure(d, Child)
d2 = cattr.unstructure(p2, Child)
print(d2)
finally:
# This does not
d = cattr.unstructure(p, Parent)
p2 = cattr.structure(d, Parent)
d2 = cattr.unstructure(p2, Parent)
print(d2)
根据错误消息,我猜测 sqlalchemy 正在尝试检查 parent_mapper
上的 属性 'children' 是否是 Child
的列表并且它正在获取dict
而不是(它处于非结构化状态)。我什至不知道 sqlalchemy 是如何进行这项检查的。再次猜测,我想它总是会做一些一致性检查。无论如何,我现在知道如何解决这个问题了。
所以我仔细研究了 SQLAlchemy 文档,发现并改编了 an example 让您的代码工作:
from dataclasses import dataclass
from typing import List
import cattr
from sqlalchemy.orm import (
relationship,
sessionmaker,
registry,
)
from sqlalchemy import Table, Column, ForeignKey, create_engine, MetaData
from sqlalchemy.sql.sqltypes import Integer, String
# commented this out for the example
# from src.vlep.domain.model import Patient
mapper_registry = registry()
@dataclass
class Child:
name: str
@dataclass
class Parent:
name: str
children: List[Child]
metadata_obj = MetaData()
parent = Table(
"parent",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("name", String(255)),
)
child = Table(
"child",
metadata_obj,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("name", String(255)),
Column("parent_id", ForeignKey("parent.id", ondelete="SET NULL")),
)
def start_mappers():
mapper_registry.map_imperatively(
Parent,
parent,
properties={"children": relationship(Child, backref="parent")},
)
mapper_registry.map_imperatively(Child, child)
if __name__ == "__main__":
engine = create_engine("sqlite:///:memory:")
metadata_obj.create_all(engine)
start_mappers()
session = sessionmaker(bind=engine)()
c1 = Child("Uguinho")
c2 = Child("Luizinho")
p = Parent("Fulano", [c1, c2])
session.add(p)
session.commit()
try:
# This works
d = cattr.unstructure(p, Child)
p2 = cattr.structure(d, Child)
d2 = cattr.unstructure(p2, Child)
print(d2)
finally:
# This now works
d = cattr.unstructure(p, Parent)
p2 = cattr.structure(d, Parent)
d2 = cattr.unstructure(p2, Parent)
print(d2)
输出:
{'name': 'Fulano'}
{'name': 'Fulano', 'children': [{'name': 'Uguinho'}, {'name': 'Luizinho'}]}
我认为可能主要问题之一是您将“backref”声明为 backref=backref('parents')
,而那里的函数包装器是不必要的?不太确定...
无论如何,数据类现在用于正确映射(据我所知)。我希望这个例子对你有用!