Django JsonField 按两个字段过滤
Django JsonField filter by two fields
class Blog:
values = JSONField(blank=True, default=list)
[
{
"id": 1,
"value": "31"
},
{
"id": 2,
"value": "Hello"
},
...
]
我需要获取 ID 为 1 且该字段的值大于 31 的所有对象。
我试过 q = queryset.filter(values__0__id=1, values__0__value_gte=31)
但它仅适用于对象,如果我需要的对象仅位于第一个元素中。
您好,欢迎来到 Stack Overflow!
尝试查看 Django 的 Q 对象。一些文档可用 here.
案例一
如 this 回答中所述,尝试使用 __contains
过滤器:
Blog.objects.filter(values__contains=[{'id': 1}])
然后手动筛选第二个字段的结果。
案例二
也许更好的选择是为单个值设置第二个 table,例如以下模型:
class Blog(models.Model):
name = models.CharField(max_length=100) # or something else
class Value(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
json = models.JSONField(blank=True, related_name="values")
然后像这样执行搜索:
Blog.objects.filter(Q(values__json__id=1) & Q(values__json__value__gte=31))
显然,现在 Django
没有内置支持 JSONField
的数组元素比较。幸运的是,Django
允许制作很多自定义员工。例如,Django
的 raw SQL 特征。
如果您使用 PostgreSQL 作为主数据库,则可以使用 JSON Processing Functions。来自 JSON 处理函数的 jsonb_array_elements()
是一个不错的选择。
结合以上特点,我们可以为您的情况做一些解决方法:
# Create method that uses raw SQL within your `objects` Model Manager.
# Create your own filters as you want. This example should be improved
# and handle exception cases of course.
def filter_blogs_json(json_field, sql_operator, value):
return Blog.objects.raw(f"SELECT id, data FROM yourappname_blog CROSS JOIN jsonb_array_elements(values) AS data WHERE (data->'{json_field}')::numeric {sql_operator} {value};")
# You can get raw objects queryset
raw_blogs_qs = filter_blogs_json('value', '>=', 31)
# Then you can process it anyway you want
filtered_blog_ids = [b.id for b in raw_blogs_qs]
queryset = Blog.objects.filter(...).filter(id__in=filtered_blog_ids)
很简单,不是吗? :)
此外,我相信可以为 JSONField
创建自己的查询集 Lookup,根据需要扩展查询等
class Blog:
values = JSONField(blank=True, default=list)
[
{
"id": 1,
"value": "31"
},
{
"id": 2,
"value": "Hello"
},
...
]
我需要获取 ID 为 1 且该字段的值大于 31 的所有对象。
我试过 q = queryset.filter(values__0__id=1, values__0__value_gte=31)
但它仅适用于对象,如果我需要的对象仅位于第一个元素中。
您好,欢迎来到 Stack Overflow!
尝试查看 Django 的 Q 对象。一些文档可用 here.
案例一
如 this 回答中所述,尝试使用 __contains
过滤器:
Blog.objects.filter(values__contains=[{'id': 1}])
然后手动筛选第二个字段的结果。
案例二
也许更好的选择是为单个值设置第二个 table,例如以下模型:
class Blog(models.Model):
name = models.CharField(max_length=100) # or something else
class Value(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
json = models.JSONField(blank=True, related_name="values")
然后像这样执行搜索:
Blog.objects.filter(Q(values__json__id=1) & Q(values__json__value__gte=31))
显然,现在 Django
没有内置支持 JSONField
的数组元素比较。幸运的是,Django
允许制作很多自定义员工。例如,Django
的 raw SQL 特征。
如果您使用 PostgreSQL 作为主数据库,则可以使用 JSON Processing Functions。来自 JSON 处理函数的 jsonb_array_elements()
是一个不错的选择。
结合以上特点,我们可以为您的情况做一些解决方法:
# Create method that uses raw SQL within your `objects` Model Manager.
# Create your own filters as you want. This example should be improved
# and handle exception cases of course.
def filter_blogs_json(json_field, sql_operator, value):
return Blog.objects.raw(f"SELECT id, data FROM yourappname_blog CROSS JOIN jsonb_array_elements(values) AS data WHERE (data->'{json_field}')::numeric {sql_operator} {value};")
# You can get raw objects queryset
raw_blogs_qs = filter_blogs_json('value', '>=', 31)
# Then you can process it anyway you want
filtered_blog_ids = [b.id for b in raw_blogs_qs]
queryset = Blog.objects.filter(...).filter(id__in=filtered_blog_ids)
很简单,不是吗? :)
此外,我相信可以为 JSONField
创建自己的查询集 Lookup,根据需要扩展查询等