如何使SQLAlchemy hybrid_property select表达式同时适用于select和fi

2024-04-19 03:56:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直试图写一些混合属性的表达式,但我发现它们非常有限,我想知道我是否可以绕过这些限制。在

基本上,我发现它们要么与session.query(Model.hybrid_property)或{}一起工作,但不是两者都能工作。在

这是我的意思的一个例子,假设有两行叫做value1value2,而{}是hybrid_property。在

# With as_scalar()
>>> session.query(Model).filter(Model.value=='value1').all()
[([<__main__.Model object],)]         # this is wanted
>>> session.query(Model.value).all()
[(u'value1',)]

# Without as scalar()
>>> session.query(Model).filter(Model.value=='value1').all()
[]
>>> session.query(Model.value).all()
[(u'value1',), (u'value2',)]          # this is wanted

取决于是否使用as_scalar(),它会改变它的工作方式。有没有一种方法可以同时使用这两种方法?在

下面是一些示例代码(经过编辑以显示完全不起作用的示例):

^{pr2}$

其结果是:

[u'value1', u'value2']
[(u'value1',), (u'value2',)]
[]

[u'value1', u'value2']
[(u'value1',)]
[<__main__.Model object at 0x041D5C90>]

Tags: modelobjectvaluemainsessionaspropertyall
1条回答
网友
1楼 · 发布于 2024-04-19 03:56:49

您看到的变化是由于表达式返回的对象类型和表达式的使用位置造成的。在

没有as_scalar():

表达式返回一个Select对象。在

session.query(Model.value).all()中,您的表达式被传递给session.query(),而the docs可以接受:

a sequence of entities and/or SQL expressions.

。。。所以没关系。我们可以用这个简单的问题证明:

print(session.query(select([1])).all())  # [(1,)]

在第二个查询session.query(Model).filter(Model.value == "value1").all()中,您现在使用的是等式比较左侧的Select,然后该比较的结果被传递给query.filter()。SQLAlchemy使用丰富的比较来比较列(like)元素,方法是在Column上重载__eq__()方法,您可以自己看到:

^{pr2}$

但表达式返回一个Select对象:

print(Select.__eq__)  # <slot wrapper '__eq__' of 'object' objects>
# which is just the same __eq__ method that every python object has, defined on object
print(Select.__eq__ is object.__eq__)  # True

既然我们知道Select.__eq__()方法还没有重载,那么Select对象和字符串之间的==比较会产生什么结果呢?{cd13>总是^。当我们将False作为查询的唯一筛选器时会发生什么情况?公司名称:

print(session.query(Model).filter(False).all())
# SELECT "Model".row_id AS "Model_row_id", "Model".relation_id AS "Model_relation_id" FROM "Model" WHERE 0 = 1

WHERE 0 = 1始终计算为false,因此查询始终为空。在

使用as_scalar():

the docsSelect.as_scalar()

return a ‘scalar’ representation of this selectable, which can be used as a column expression.

Typically, a select statement which has only one column in its columns clause is eligible to be used as a scalar expression.

The returned object is an instance of ScalarSelect.

因此,在这个scanario中,表达式返回一个ScalarSelect对象,它可以像列一样对待。在

首先,解决.filter(Model.value_scalar=='value1')查询行为之间的差异:

print(ScalarSelect.__eq__ is Column.__eq__)  # True

ScalarSelect的__eq__()实现与Column相同,这意味着在Query.filter()的上下文中,相等性测试会产生一些有意义的东西:

print(Model.value_scalar == "value1")
# (SELECT "ModelRelation".name FROM "ModelRelation", "Model" WHERE "ModelRelation".row_id = "Model".relation_id) = :param_1

因此,在这种情况下,查询会产生合理的结果。在

但是,在session.query(Model.value_scalar).all()的情况下,它只返回一个值,即使表中有两行。在

此查询生成的sql是:

SELECT (SELECT "ModelRelation".name
FROM "ModelRelation", "Model"
WHERE "ModelRelation".row_id = "Model".relation_id) AS anon_1

ScalarSelect被解释为一个列时,它本身是被选中的,而不是被选中的,就像在no as_scalar()的情况下一样。为什么SELECT (SELECT...) AS anon_1只从查询中返回一行,但我可以向您展示它发生在数据库级别,它不是sqlalchemy处理结果,而且出于某种原因只返回一个结果。这将通过原始dbapi连接执行相同的查询:

^{8}$

因此,当表达式返回一个Column时,您将获得最一致的行为。在

文档中有一个关于Join Dependent Hybrid Relationships的部分,其中它只使用相关的对象列作为表达式值,但您需要在查询中提供联接。在

如果模型是:

class Model(Base):
    __tablename__ = "Model"
    row_id = Column(Integer, primary_key=True)
    relation_id = Column(Integer, ForeignKey("ModelRelation.row_id"))

    relation = relationship("ModelRelation")

    @hybrid_property
    def value(self):
        return self.relation.name

    @value.expression
    def value(cls):
        return ModelRelation.name

此查询:session.query(Model.value).all()呈现为

SELECT "ModelRelation".name AS "ModelRelation_name" FROM "ModelRelation"

…并按预期返回[('value1',), ('value2',)]。在

但是这个查询:session.query(Model).filter(Model.value == "value1").all()呈现为:

SELECT "Model".row_id AS "Model_row_id", "Model".relation_id AS "Model_relation_id" 
FROM "Model", "ModelRelation" 
WHERE "ModelRelation".name = ?

…但是返回两行,即使我们已经过滤了值:[<__main__.Model object at 0x000002060369FEC8>, <__main__.Model object at 0x000002060348B108>]。在

在这部分文档中,他们处理的模型称为User和{},他们说:

However, at the expression level, it’s expected that the User class will be used in an appropriate context such that an appropriate join to SavingsAccount will be present

因此,如果我们进行查询session.query(Model).join(ModelRelation).filter(Model.value == "value1").all(),则呈现的查询将变成:

^{12}$

…并返回正确的1结果:[<__main__.Model object at 0x000001606F030D48>]。在

文档接着描述了另一个例子Correlated Subquery Relationship Hybrid,但是我发现当select()是查询的目标实体时,它具有与上面完全相同的限制,因为它只返回单个结果。在

相关问题 更多 >