如何获取对应于SQLAlchemy InstrumentedAttribute的属性值?
假设你有一个用SQLAlchemy映射的类叫做Table
,还有这个类的一个实例t
。那么,怎么才能获取t.colname
的值,这个值对应于Table.colname
这个sqlalchemy.org.attributes.InstrumentedAttribute
实例呢?
如果我想用Column
来问同样的问题,而不是InstrumentedAttribute
,那该怎么做呢?
假设你有一个ORDER BY
子句中的列的列表,还有一行数据,我想找到在这个排序中,位于该行之前或之后的前n行数据。
2 个回答
这里有一个例子,即使你使用的字段名称和表中定义的不同也没关系。
示例模型
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.
要获取一个对象的属性值,特别是和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中。