Django 自动将类似数字的值转换为数字和数组索引:
让我们比较原始的SQLs
print(Foo.objects.filter(node_status__has_key='dog').query)
print(Foo.objects.filter(node_status__has_key='2').query)
SELECT `django4_foo`.`id`, `django4_foo`.`node_status` FROM `django4_foo` WHERE JSON_CONTAINS_PATH(`django4_foo`.`node_status`, 'one', $."dog")
SELECT `django4_foo`.`id`, `django4_foo`.`node_status` FROM `django4_foo` WHERE JSON_CONTAINS_PATH(`django4_foo`.`node_status`, 'one', $[2])
如您所见,2 被转换为$[2],但dog 被转换为$."dog"
一种可能的解决方案是获取原始 SQL,手动重新格式化并使用原始 SQL 初始化新的查询集:
raw_sql = str(Foo.objects.filter(node_status__has_key='2').query).replace(f'$[2]', f'\'$.2\'')
print(raw_sql)
rqs = Foo.objects.raw(raw_sql)
for o in rqs:
print(o.node_status)
SELECT `django4_foo`.`id`, `django4_foo`.`node_status` FROM `django4_foo` WHERE JSON_CONTAINS_PATH(`django4_foo`.`node_status`, 'one', '$.2')
{'cat': '1', '2': True, 'dog': True}
但是,此 hack 不适用于 node_status__2=True 过滤器。
让我们检查这个查询是否有 dog 键:
print(Foo.objects.filter(node_status__dog=True).query)
输出是:
SELECT `django4_foo`.`id`, `django4_foo`.`node_status` FROM `django4_foo` WHERE JSON_UNQUOTE(JSON_EXTRACT(`django4_foo`.`node_status`, $."dog")) = JSON_EXTRACT(true, '$')
但实际工作的原始 SQL 是:
SELECT `django4_foo`.`id`, `django4_foo`.`node_status` FROM `django4_foo` WHERE JSON_UNQUOTE(JSON_EXTRACT(`django4_foo`.`node_status`, "$.dog")) = 'True'
所以filter → raw 转换存在很多问题。 (Django 社区也不建议使用 str(query) 生成原始 SQL 查询)
Django 的定位就像适用于所有类型螺丝的万能螺丝刀。内部冲突和过于复杂的“通用且多合一”逻辑是 Django 的常见问题。
是的,可以覆盖query 类和change key transformation logic,但它很难阅读和理解,非常复杂,并且自定义逻辑可能会出现另一个问题(因为 Django 不希望逻辑被更改)。
所以更简单的方法是:
- 只需使用
Foo.objects.raw(sql_query),其中sql_query 是手动预定义的字符串
- 使用
cursor.execute(sql_query, \[params\]) 和sql_query 预定义与①相同
- 获取
Foo.objects.all() 并使用 python 逻辑对其进行过滤 ([e for e in Foo.objects.all() if '2' in e.node_status])
其他可能的方式有:
※ 我的选择;)