SQLAlchemy 'entity' for `add_columns` 没有 table 支持
SQLAlchemy 'entity' for `add_columns` not backed by a table
使用像这样的 SQLAlchemy 查询:
result = db.session.query(Model).add_columns(
func.min(Model.foo).over().label("min_foo"),
func.max(Model.foo).over().label("max_foo"),
# ...
)
结果是一个可迭代的元组,首先由 Model
行组成,然后是添加的列。
我怎样才能:
- 将添加的列贡献给
Model
,这样就可以像 model.min_foo
等一样从每个元素访问它们;或者
- 将添加的列映射到一个单独的数据类中,这样它们就可以像
extra.min_foo
?
我在这里试图实现的主要目标是按名称访问 - 例如给定的标签 - 而不是将它们全部枚举为 model, min_foo, max_foo, ...
并依赖于保持相同的顺序。对于 model, *extra
,extra
只是聚合值的普通列表,没有对标签的引用。
如果我先将列动态添加到模型中:
Model.min_foo = Column(Numeric)
然后它抱怨:
Implicitly combining column modeltable.min_foo with column modeltable.min_foo under attribute 'min_foo'.
Please configure one or more attributes for these same-named columns explicitly
Apparently the solution 即显式加入 table。但这不是一个!
看来 'mappers' 应该可以做到这一点,但我找不到任何未明确映射到 'table name' 或其列的示例,我没有真的在这里 - 我不清楚 if/how 它们可以与聚合一起使用,或者查询中实际上没有存储在任何 table.[=24= 中的其他 'virtual' 列]
我认为你要找的是 Query-time SQL expressions as mapped attributes:
from sqlalchemy import create_engine, Column, Integer, select, func
from sqlalchemy.orm import (Session, declarative_base, query_expression,
with_expression)
Base = declarative_base()
class Model(Base):
__tablename__ = 'model'
id = Column(Integer, primary_key=True)
foo = Column(Integer)
foo2 = Column(Integer, default=0)
engine = create_engine('sqlite:///', future=True)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Model(foo=10))
session.add(Model(foo=20))
session.add(Model(foo=30))
session.add(Model(foo=40))
session.add(Model(foo=50, foo2=1))
session.add(Model(foo=60, foo2=1))
session.add(Model(foo=70, foo2=1))
session.add(Model(foo=80))
session.add(Model(foo=90))
session.add(Model(foo=100))
session.commit()
Model.min_foo = query_expression(func.min(Model.foo).over())
stmt = select(Model).where(Model.foo2 == 1)
models = session.execute(stmt).all()
for model, in models:
print(model.min_foo)
with Session(engine) as session:
Model.max_foo = query_expression()
stmt = select(Model).options(with_expression(Model.max_foo,
func.max(Model.foo).over())
).where(Model.foo2 == 1)
models = session.execute(stmt).all()
for model, in models:
print(model.max_foo)
您可以在定义 query_expression
时定义一个 default
表达式,或者使用 .options
和 with_expression
您可以定义一个运行时表达式。唯一的问题是 Mapped 属性无法取消映射,并且将 return None
for max_foo
因为没有定义默认表达式。
使用像这样的 SQLAlchemy 查询:
result = db.session.query(Model).add_columns(
func.min(Model.foo).over().label("min_foo"),
func.max(Model.foo).over().label("max_foo"),
# ...
)
结果是一个可迭代的元组,首先由 Model
行组成,然后是添加的列。
我怎样才能:
- 将添加的列贡献给
Model
,这样就可以像model.min_foo
等一样从每个元素访问它们;或者 - 将添加的列映射到一个单独的数据类中,这样它们就可以像
extra.min_foo
?
我在这里试图实现的主要目标是按名称访问 - 例如给定的标签 - 而不是将它们全部枚举为 model, min_foo, max_foo, ...
并依赖于保持相同的顺序。对于 model, *extra
,extra
只是聚合值的普通列表,没有对标签的引用。
如果我先将列动态添加到模型中:
Model.min_foo = Column(Numeric)
然后它抱怨:
Implicitly combining column modeltable.min_foo with column modeltable.min_foo under attribute 'min_foo'. Please configure one or more attributes for these same-named columns explicitly
Apparently the solution 即显式加入 table。但这不是一个!
看来 'mappers' 应该可以做到这一点,但我找不到任何未明确映射到 'table name' 或其列的示例,我没有真的在这里 - 我不清楚 if/how 它们可以与聚合一起使用,或者查询中实际上没有存储在任何 table.[=24= 中的其他 'virtual' 列]
我认为你要找的是 Query-time SQL expressions as mapped attributes:
from sqlalchemy import create_engine, Column, Integer, select, func
from sqlalchemy.orm import (Session, declarative_base, query_expression,
with_expression)
Base = declarative_base()
class Model(Base):
__tablename__ = 'model'
id = Column(Integer, primary_key=True)
foo = Column(Integer)
foo2 = Column(Integer, default=0)
engine = create_engine('sqlite:///', future=True)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add(Model(foo=10))
session.add(Model(foo=20))
session.add(Model(foo=30))
session.add(Model(foo=40))
session.add(Model(foo=50, foo2=1))
session.add(Model(foo=60, foo2=1))
session.add(Model(foo=70, foo2=1))
session.add(Model(foo=80))
session.add(Model(foo=90))
session.add(Model(foo=100))
session.commit()
Model.min_foo = query_expression(func.min(Model.foo).over())
stmt = select(Model).where(Model.foo2 == 1)
models = session.execute(stmt).all()
for model, in models:
print(model.min_foo)
with Session(engine) as session:
Model.max_foo = query_expression()
stmt = select(Model).options(with_expression(Model.max_foo,
func.max(Model.foo).over())
).where(Model.foo2 == 1)
models = session.execute(stmt).all()
for model, in models:
print(model.max_foo)
您可以在定义 query_expression
时定义一个 default
表达式,或者使用 .options
和 with_expression
您可以定义一个运行时表达式。唯一的问题是 Mapped 属性无法取消映射,并且将 return None
for max_foo
因为没有定义默认表达式。