You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SQLAlchemy中查询存储为字符串的JSON属性?

基于字符串类型JSON字段过滤SQLAlchemy查询结果

问题背景

我有一个City表,结构如下:

City
id(Integer),
name(String),
metadata(String)

表中数据示例:

1, "London", "{'lat':51.5072, 'lon':0.1276, 'Mayor': 'Sadiq Khan', 'participating':True}",
2, "Liverpool", "{'lat':53.4084, 'lon':2.9916, 'Mayor': 'Joanne Anderson', 'participating':False}",
3, "Manchester", "{'lat':53.4808, 'lon':2.2426, 'Mayor': 'Donna Ludford', 'participating':True}"

metadata是字符串类型,内部是JSON格式。我需要筛选出participating值为True的记录,预期结果是London和Manchester的两条数据。

我尝试了以下代码:

db.session.query(City).filter(cast(eval(metadata['participating]), String) == True)

但报错:

NotImplementedError: Operator 'getitem' is not supported on this expression

解决方案

错误原因

不能在SQLAlchemy过滤条件里用eval(),因为eval()是Python本地函数,SQLAlchemy无法将其转换为数据库能执行的SQL语句,这才导致了报错。

具体解决方法

根据你使用的数据库类型,选择对应的方案:

1. 支持JSON函数的数据库(PostgreSQL/MySQL 5.7+等)

利用数据库原生的JSON处理函数,SQLAlchemy可以通过func调用这些函数:

  • PostgreSQL 写法:
    先将字符串类型的metadata转为jsonb,再提取指定字段的值:

    from sqlalchemy import func
    
    results = db.session.query(City).filter(
        func.jsonb_extract_path_text(City.metadata, 'participating') == 'True'
    ).all()
    
  • MySQL 写法:
    使用JSON_EXTRACT提取字段,注意布尔值在MySQL JSON中的存储形式:

    from sqlalchemy import func
    
    # 方法1:直接匹配数值型布尔值(MySQL中True存为1,False存为0)
    results = db.session.query(City).filter(
        func.json_extract(City.metadata, '$.participating') == 1
    ).all()
    
    # 方法2:提取后转字符串匹配
    results = db.session.query(City).filter(
        func.json_unquote(func.json_extract(City.metadata, '$.participating')) == 'True'
    ).all()
    

2. 通用字符串匹配法(不依赖数据库JSON函数)

如果你的数据库不支持JSON函数,可以用字符串包含匹配,但这种方法存在误匹配风险(比如字段中存在相似字符串时),仅作为临时方案:

results = db.session.query(City).filter(
    City.metadata.contains("'participating':True")
).all()

3. 最优方案:修改模型字段类型

如果数据库支持JSON类型,直接将metadata字段改为JSON类型,后续查询会更简洁:

from sqlalchemy import JSON

class City(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    metadata = db.Column(JSON)  # 替换原String类型为JSON

修改后直接通过键值查询即可:

results = db.session.query(City).filter(City.metadata['participating'] == True).all()

内容的提问来源于stack exchange,提问作者Gerry Volta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:38:13