如何获取对应于SQLAlchemy InstrumentedAttribute的属性值?

4 投票
2 回答
5851 浏览
提问于 2025-04-15 15:52

假设你有一个用SQLAlchemy映射的类叫做Table,还有这个类的一个实例t。那么,怎么才能获取t.colname的值,这个值对应于Table.colname这个sqlalchemy.org.attributes.InstrumentedAttribute实例呢?

如果我想用Column来问同样的问题,而不是InstrumentedAttribute,那该怎么做呢?

假设你有一个ORDER BY子句中的列的列表,还有一行数据,我想找到在这个排序中,位于该行之前或之后的前n行数据。

2 个回答

0

这里有一个例子,即使你使用的字段名称和表中定义的不同也没关系。

示例模型

class MyModel(Base):
    __tablename__ = 'my_table'
    
    id = Column(Integer, primary_key=True)
    dest = Column(String,nullable=False)
    state = Column(Integer)
    meta = Column("metadata",JSONB)

如何检查

class_attributes = vars(MyModel)

# Iterate over the attributes and check if they are columns
for attr_name, attr_value in class_attributes.items():
    if isinstance(attr_value, InstrumentedAttribute):
        table_column_name = attr_value.expression.key
        model_column_name = attr_value.key
        print(f"{model_column_name} is a column with database name: {table_column_name}.")

预期输出

id is a column with database name: id. 
dest is a column with database name: dest. 
state is a column with database name: state. 
meta is a column with database name: metadata. 
7

要获取一个对象的属性值,特别是和InstrumentedAttribute相关的属性,通常只需要从它的ColumnProperty中获取属性的键,然后从对象中取出这个值就可以了。

t.colname == getattr(t, Table.colname.property.key)

如果你有一个Column,事情可能会变得复杂一些,因为对应这个Column的属性可能有不同的键。目前似乎没有公开的接口可以直接从一个列获取到对应的属性。不过,如果你不需要考虑所有的情况,可以直接使用Column.key来获取属性。

如果你想支持降序排列,你需要在函数内部构造desc(),或者稍微研究一下非公开的接口。降序修饰符的类是ClauseElement,具体来说是sqlalchemy.sql.expression._UnaryExpression。要判断是否是降序,你需要检查.modifier属性是否是sqlalchemy.sql.operators.desc_op。如果是这样,你可以通过.element属性获取到里面的列。不过,正如你所看到的,这是一个私有类,所以在升级版本时要注意这方面的变化。

检查降序仍然不能覆盖所有情况。要全面支持任意排序,需要能够重写完整的SQL表达式树,将表的引用替换为对象中的相应值。不幸的是,目前通过公开的接口无法做到这一点。遍历和重写的部分使用sqlalchemy.sql.visitors.ReplacingCloningVisitor是比较简单的,复杂的部分在于如何根据继承层次、连接映射、别名等来确定哪个列对应哪个属性。我要尝试实现这个访问者,也许我能想出一些足够稳健的东西,值得集成到SQLAlchemy中。

撰写回答