python - 生成列的 SQLAlchemy 动态构造
问题描述
我想动态派生server_default
设置为Computed(...)
.
进入的语句Computed(...)
必须从其他列派生,因此需要在映射s后访问该类:relationship
from uuid import uuid4
from sqlalchemy import func, Computed, ForeignKey, Column
from sqlalchemy.orm import relationship
from sqlalchemy.sql.base import Executable
from sqlalchemy.ext.declarative import as_declarative
from sqlalchemy.dialects.postgresql import INTEGER, UUID
def aggregate_computed(relationship: str, column: Column, expr: Executable):
...
@as_declarative()
class Base: pass
class Other(Base):
__tablename__ = "other"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
this = relationship("This", back_populates="other")
this_id = Column(UUID(as_uuid=True), ForeignKey("this.id"))
class This(Base, IDMixin):
__tablename__ = "this"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
other = relationship("Other", back_populates="this")
other_count = aggregate_computed("species", Column(INTEGER), func.count(1))
assert isinstance(This.other_count.server_default, Computed)
assert This.other_count.server_default.sqltext == """\
SELECT count(1) AS out
FROM other
WHERE this.id = other.this_id
"""
查询构造如下:
-- this is the decorated function
SELECT count(1) AS out
-- this is derived from the relationships.
-- for a m-to-n relation, it would make use of the `secondary` table
FROM other WHERE this.id = other.this_id
我知道如何从关系中构造查询,但我不知道如何告诉 SQLAlchemy 如何映射除我的属性之外的所有内容,然后在允许这些属性访问类的关系的同时完成映射。
这里的答案不是:
- “只使用一个
column_property
”:那些只接受完成的列,并且不允许通过访问映射类来构造它们 - “使用
sqlalchemy_utils.aggregate
”:那个是通过after_flush
触发器实现的,而不是GENERATED ALWAYS AS
。
解决方案
Computed
通过创建可以传递给列的子类,这确实相对容易。该子类定义sqltext
为属性而不是实例变量。由于该属性仅在映射后才能访问,因此它可以访问完全映射的模型类。
不幸的是,我真正想做的事情是不可能的,因为生成的语句在 PostgreSQL 中无效(GENERATED ALWAYS AS
不能使用嵌套查询)。
然而,该方法可能对其他动态生成的GENERATED ALWAYS AS
语句很有用。
class ComputedAggregate(Computed):
def __init__(self, relationship: str, expr, *, persisted=None):
# do not call super().__init__, as it tries to set self.sqltext
self.relationship = relationship
self.expr = expr
self.persisted = persisted
self.column = None
@property
def sqltext(self):
cls = get_class_by_table(MyBase, self.column.table)
relationships: t.List[RelationshipProperty] = list(
reversed(path_to_relationships(self.relationship, cls))
)
query = select_correlated_expression(
cls,
self.expr,
self.relationship,
relationships[0].mapper.class_,
)
return query.as_scalar()
推荐阅读
- svelte - 如何在 Svelte 中添加上滑效果来删除记录?
- ios - 我可以在连接到 Windows 台式电脑的物理 Iphone 7 上运行颤振应用程序吗?
- python - 删除二维numpy数组中彼此特定距离的点
- ios - 补充视图ForElementKind 的结果无效
- java - simpledate 格式化程序格式化返回不正确。格式化后扣除的日期
- c++ - 如何比 for 循环更有效地初始化数组?C++
- c - 如何编写程序来检查输入是否等于标准值
- ruby-on-rails - Active Storage public_url 不返回 key/filename.extension
- reactjs - 在分组列中显示堆叠的图例 highcharts-RecatJs highcharts
- shopify - 如何使用 product.media.size 返回要在 product.media 数组中使用的整数