获取 sqlalchemy 基类对象而不是子对象

Posted

技术标签:

【中文标题】获取 sqlalchemy 基类对象而不是子对象【英文标题】:Get sqlalchemy base class object instead of children 【发布时间】:2018-11-24 12:10:52 【问题描述】:

我的模型中有三个类,其中一个类被另外两个继承:

class Item(Base):
    __tablename__ = 'item'

    id = Column(Integer, primary_key=True)
    title = Column(Unicode(300))
    type = Column(Unicode(50))

    __mapper_args__ = 
        'polymorphic_on': type
    


class Note(Item):
    __tablename__ = 'note'

    id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    extra = Column(Text)

    __mapper_args__ = 
        'polymorphic_identity': 'note'
    


class Task(Item):
    __tablename__ = 'task'

    id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    another_extra = Column(Text)

    __mapper_args__ = 
        'polymorphic_identity': 'task'
    

所以,当我执行 session.query(Item).all() 时,我得到一个包含 NoteTask 对象的列表,但我不希望这样,我希望我的对象是 Item 类的实例,并且只是有idtitletype,而不是那些额外的字段。我应该如何编写查询?

为了澄清更多,目前,我得到:

[
     <models.Note object at 0x7f25ac3ffe80>,
     <models.Task object at 0x7f25ac3ffe80>,
     <models.Task object at 0x7f25ac3ffe80>,
     ...
]

但我想得到:

[
    <models.Item object at 0x000000000000>,
    <models.Item object at 0x000000000000>,
    <models.Item object at 0x000000000000>,
    ...
]

【问题讨论】:

仅仅query for those specific columns 并得到一个命名元组就足够了吗?还是您绝对需要回复Item @SuperShoot 我需要它们成为Item 类的实例。顺便谢谢你的喜欢。 不确定它是否会帮助您,但我注意到如果您删除多态身份,它会按预期工作。 代码不能为我运行(得到:“没有定义这样的 polymorphic_identity '类型'”)(?)但根据我认为just_item = with_polymorphic(Item, [Note, Task]) 的文档,然后是:session.query(just_item).all() 可能工作?见:docs.sqlalchemy.org/en/latest/orm/inheritance_loading.html @HansBouwmeester with_polymorphic(Item, [Note, Task]) 确保在查询基类时,属于类列表(本例中为NoteTask)的属性被预先加载,以便生成的对象以后不必延迟加载属性值。从该查询返回的对象仍将是 NoteTask 对象。 【参考方案1】:

注意:这在多线程应用程序中可能会出现问题。

您可以使用上下文管理器来临时阻止多态性:

from contextlib import contextmanager
from sqlalchemy import inspect

@contextmanager
def no_poly(class_):
    mapper = inspect(class_).mapper
    polycol = mapper.polymorphic_on
    mapper.polymorphic_on = None
    yield class_
    mapper.polymorphic_on = polycol

Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
task = Task(title='Task Title', another_extra='something')
s = Session()
s.add(task)
s.commit()

# opening a new session as if the pk already exists in the 
# identity map it will return whatever type that pk is 
# pointing at.
s = Session() 
with no_poly(Item) as class_:
    inst = s.query(class_).all()
    print(inst)  # [<__main__.Item object at 0x000001443685DDD8>]
s = Session()  # new session again.
inst = s.query(Item).all()
print(inst)  #  [<__main__.Task object at 0x00000144368770B8>]

需要注意的一点,正如我的示例中的 cmets 中所指出的,如果对象的标识已经在 Identity Map 中引用,那么您将取回其中保存的任何类型,无论该类是什么你查询。

【讨论】:

这看起来可能会给多线程程序带来一些风险,因为它会修改映射器。 IIja 是正确的,我真的不想修改mapper,我只是在寻找查询或__mapper_args__ 以某种方式工作。 @IljaEverilä 你知道如何实现这一目标吗? 是的,它不是线程安全的。我会在答案中注明。在这种情况下,这是先决条件吗?

以上是关于获取 sqlalchemy 基类对象而不是子对象的主要内容,如果未能解决你的问题,请参考以下文章

获取时间戳而不是日期时间对象,sqlalchemy python 2.7

基类对象是不是隐式添加到派生类?

使用 ID 而不是对象填充 SQLAlchemy 多对多关系

猫鼬填充如何在同一对象而不是子文档中填充文档

SQLAlchemy 子查询列表对象没有属性

在 JavaScript 中检测对象是不是有子对象