python - 在 SQLAlchemy 中使用 Postgres 索引运算符
问题描述
给定具有以下架构的表:
create table json_data (
id integer PRIMARY KEY NOT NULL,
default_object VARCHAR(10) NOT NULL,
data jsonb NOT NULL
);
对于表中的每个实体,我想检索data['first']['name']
字段的值,或者如果它是 null 值data[json_data.default_object]['name']
,或者如果后者也是 null 然后返回一些默认值。在“纯”SQL 中,我可以编写以下代码来满足我的需要:
insert into
json_data(
id,
default_object,
data
)
values(
0,
'default',
'{"first": {"name": "first_name_1"}, "default": {"name": "default_name_1"}}'
),
(
1,
'default',
'{"first": {}, "default": {"name": "default_name_2"}}'
);
select
id,
coalesce(
json_data.data -> 'first' ->> 'name',
json_data.data -> json_data.default_object ->> 'name',
'default_value'
) as value
from
json_data;
我试图将上面的“模型”“翻译”成一个 SQLAlchemy 实体:
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
Base = declarative_base()
class JsonObject(Base):
__tablename__ = 'json_data'
id = sa.Column(sa.Integer, primary_key=True)
default_object = sa.Column(sa.String(10), nullable=False)
data = sa.Column(postgresql.JSONB, nullable=False)
@hybrid_property
def name(self) -> str:
obj = self.data.get('first')
default_obj = self.data.get(self.default_object)
return (obj.get('name') if obj else default_obj.get('name')) or default_obj.get('name')
@name.setter
def name(self, value: str):
obj = self.data.setdefault('first', dict())
obj['name'] = value
@name.expression
def name(self):
return sa.func.coalesce(
self.data[('first', 'name')].astext,
self.data[(self.default_object, 'name')].astext,
'default_value',
)
但似乎name
混合属性的表达式不像我预期的那样工作。name
如果我按属性查询实体,例如:
query = session.query(JsonObject).filter(JsonObject.name == 'name')
由query
SQLAlchemy 扩展为如下内容:
SELECT json_data.id AS json_data_id, json_data.default_object AS json_data_default_object, json_data.data AS json_data_data
FROM json_data
WHERE coalesce((json_data.data #> %(data_1)s), (json_data.data #> %(data_2)s), %(coalesce_1)s) = %(coalesce_2)s
它使用路径运算符而不是索引运算符。我应该怎么做才能让 SQLAlchemy 创建一个表达式,就像我在问题开头写的那样?
解决方案
好的,我找到的解决方案非常简单。正如 SQLAlchemy 文档所说:
索引操作返回一个表达式对象,其类型默认为 JSON,因此可以在结果类型上调用进一步的面向 JSON 的指令。
因此,我们可以使用“链式”python 索引运算符。所以下面的代码在我看来是合法的:
class JsonObject(Base):
# Almost the same stuff, except for the following:
@name.expression
def name(self):
return sa.func.coalesce(
self.data['first']['name'].astext,
self.data[self.default_object]['name'].astext,
'default_value',
)
推荐阅读
- slack-block-kit - Slack block ui kit - 在一行中有两个静态选择和松弛模态
- excel - 如何使用 VBA Excel 调整在电子邮件正文上粘贴为图片的一系列单元格的大小
- c++ - constexpr 变量必须由 const 表达式初始化
- python - 在python中将列值分组为稀疏矩阵
- blazor - 将所有 GET/页面请求保存到 Blazor 中的数据库
- css - 字体粗细未在 CSS 中应用
- kotlin - Kotlin 如何存储列表的数据?
- javascript - 在由两个数据库字段组成的表列中查找值
- nginx - 302 重定向:具有 Mime 类型的 SVG 图像:文本/纯文本 | Ghost、Nginx 和 Cloudflare 设置
- reactjs - 将数据从组件传递到另一个组件