栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

在SQLAlchemy关系上设置delete-orphan会导致AssertionError:此AttributeImpl未配置为跟踪父级

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

在SQLAlchemy关系上设置delete-orphan会导致AssertionError:此AttributeImpl未配置为跟踪父级

好的,在这种情况下,您需要仔细查看,尽管这里有一个警告可能会成为例外,我将对此进行调查。这是您的示例的有效版本:

from sqlalchemy.ext.associationproxy import association_proxyfrom sqlalchemy import *from sqlalchemy.orm import *from sqlalchemy.ext.declarative import declarative_basebase= declarative_base()tagging = Table('tagging',base.metadata,    Column('tag_id', Integer, ForeignKey('tag.id', ondelete='cascade'), primary_key=True),    Column('role_id', Integer, ForeignKey('role.id', ondelete='cascade'), primary_key=True))class Tag(base):    __tablename__ = 'tag'    id = Column(Integer, primary_key=True)    name = Column(String(100), unique=True, nullable=False)    def __init__(self, name=None):        self.name = nameclass Role(base):    __tablename__ = 'role'    id = Column(Integer, primary_key=True)    tag_names = association_proxy('tags', 'name')    tags = relationship('Tag',   secondary=tagging,   cascade='all,delete-orphan',   backref=backref('roles', cascade='all'))e = create_engine("sqlite://", echo=True)base.metadata.create_all(e)s = Session(e)r1 = Role()r1.tag_names.extend(["t1", "t2", "t3"])s.add(r1)s.commit()

现在开始运行:

... creates tables/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/properties.py:918: SAWarning: On Role.tags, delete-orphan cascade is not supported on a many-to-many or many-to-one relationship when single_parent is not set.   Set single_parent=True on the relationship().  self._determine_direction()Traceback (most recent call last):  ... stacktrace ...  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 349, in hasparent    assert self.trackparent, "This AttributeImpl is not configured to track parents."AssertionError: This AttributeImpl is not configured to track parents.

因此,这是重要的部分: SAWarning:在Role.tags上,未设置single_parent时,多对多或多对一关系不支持删除孤立级联。
在relationship()上设置single_parent = True。

因此,错误是固定的,如果您这样说:

tags = relationship('Tag',          secondary=tagging,          cascade='all,delete-orphan',          single_parent=True,         backref=backref('roles', cascade='all'))

但是,您可能会发现,这并不是您真正想要的:

r1 = Role()r2 = Role()t1, t2 = Tag("t1"), Tag("t2")r1.tags.extend([t1, t2])r2.tags.append(t1)

输出:

sqlalchemy.exc.InvalidRequestError: Instance <Tag at 0x101503a10> is already associated with an instance of <class '__main__.Role'> via its Role.tags attribute, and is only allowed a single parent.

那就是您的“单亲”-“删除-孤立”功能仅适用于所谓的 生命周期
关系,其中子完全位于其单亲的范围内。因此,使用多对多的“孤立”几乎没有任何意义,它仅受支持,因为有些人确实非常想通过关联表获得此行为(无论是传统DB的东西)。

继承人的文档:

delete-
orphan级联意味着每个子对象一次只能有一个父对象,因此在大多数情况下都将其配置为一对多关系。将其设置为多对一或多对多关系比较麻烦;对于此用例,SQLAlchemy要求使用single_parent
= True函数配置Relationship(),该函数建立Python端验证,以确保该对象一次仅与一个父对象关联。

当您说“我要它清理孤儿”时,意味着什么?这意味着在这里,如果您要说

r1.tags.remove(t1)
,那么您说的是“flush”。SQLAlchemy会看到“ r1.tags,t1已被删除,如果它是一个孤儿,我们需要删除!好吧,所以我们开始进行“标记”,然后
扫描整个表 对于剩余的任何条目。“一次只为每个标签天真地进行操作显然是非常低效的-
如果您在一个会话中影响了数百个标签集合,那么将有数百个这样的潜在巨大查询。这增加了相当复杂的功能,因为工作单元倾向于一次考虑一个集合,并且仍然会增加人们可能并不想要的明显的查询开销。在现实中,“删除-
孤立”系统仅在将对象B从内存中的对象A分离时才起作用-无需扫描数据库或类似的东西,它是

因此,您在此处使用“删除孤儿”所做的事情是正确的,但让我们将其粘贴到事件中,并使用更高效的查询,并一次性删除我们不需要的所有内容:

from sqlalchemy.ext.associationproxy import association_proxyfrom sqlalchemy import *from sqlalchemy.orm import *from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import eventbase= declarative_base()tagging = Table('tagging',base.metadata,    Column('tag_id', Integer, ForeignKey('tag.id', ondelete='cascade'), primary_key=True),    Column('role_id', Integer, ForeignKey('role.id', ondelete='cascade'), primary_key=True))class Tag(base):    __tablename__ = 'tag'    id = Column(Integer, primary_key=True)    name = Column(String(100), unique=True, nullable=False)    def __init__(self, name=None):        self.name = nameclass Role(base):    __tablename__ = 'role'    id = Column(Integer, primary_key=True)    tag_names = association_proxy('tags', 'name')    tags = relationship('Tag',   secondary=tagging,  backref='roles')@event.listens_for(Session, 'after_flush')def delete_tag_orphans(session, ctx):    session.query(Tag).        filter(~Tag.roles.any()).        delete(synchronize_session=False)e = create_engine("sqlite://", echo=True)base.metadata.create_all(e)s = Session(e)r1 = Role()r2 = Role()r3 = Role()t1, t2, t3, t4 = Tag("t1"), Tag("t2"), Tag("t3"), Tag("t4")r1.tags.extend([t1, t2])r2.tags.extend([t2, t3])r3.tags.extend([t4])s.add_all([r1, r2, r3])assert s.query(Tag).count() == 4r2.tags.remove(t2)assert s.query(Tag).count() == 4r1.tags.remove(t2)assert s.query(Tag).count() == 3r1.tags.remove(t1)assert s.query(Tag).count() == 2

现在每次刷新时,我们都会在末尾得到以下查询:

DELETE FROM tag WHERe NOT (EXISTS (SELECt 1 FROM tagging, role WHERe tag.id = tagging.tag_id AND role.id = tagging.role_id))

因此,当我们可以按照简单的SQL准则删除对象时,就不需要将对象拉入内存以将其删除(通过痛苦的行编程,当数据库可以更有效地执行操作时,依靠将行拉入内存已被称为行)。)。与在计划器中往往更昂贵的“外部联接”相比,“不存在”在搜索相关行的缺失时也非常有效。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/639912.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号