簡體   English   中英

如何在 sqlalchemy 經典映射上使用 cattrs 構造數據類?

[英]How to structure dataclass with cattrs on sqlalchemy classical mapping?

我試圖在我使用 sqlalchemy 的應用程序中使用 cattr 構造一些類。 出於某種原因,在我向數據庫提交 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 文檔進行了一些挖掘,並找到並修改了一個使您的代碼工作的示例

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)

Output:

{'name': 'Fulano'}
{'name': 'Fulano', 'children': [{'name': 'Uguinho'}, {'name': 'Luizinho'}]}

我認為可能的主要問題之一是您將“backref”聲明為backref=backref('parents') ,而 function 包裝器是不必要的? 不太確定...

無論如何,數據類現在已正確用於映射(據我所知)。 我希望這個例子對你有用!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM