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

如何扩展SimpleListFilter实现JSON字段关键词筛选关联页面?

通过Product的JSON字段内容筛选关联的Page

我的项目中,Product信息存储在JSON字段中,且与Page模型通过多对多(m2m)关联(Page类似目录页面)。模型定义如下:

class Page(models.Model):
    ... 
    products = models.ManyToManyField(Product, related_name="%(class)s_name", related_query_name="product_page_qs", blank=True)

class Product(models.Model):
    ...
    number = models.PositiveIntegerField()
    data = models.JSONField()

我已创建SimpleListFilter实现文本搜索,可获取包含指定product.id的Page列表(功能正常):

class PageProductSearch(admin.SimpleListFilter):
    title = "product"
    parameter_name = "product"
    template = "search_filter.html"

    def lookups(self, request, model_admin):
        return ((None, None),)

    def choices(self, changelist):
        super().choices(changelist)
        return (*self.lookup_choices,)

    def queryset(self, request, queryset):
        value = self.value()
        if value:
            return queryset.filter(products=value)

当前问题是必须知晓product.id才能筛选,想扩展查询集实现对JSON字段中键值的搜索,尝试了两种方法均失败:

  • 使用queryset.filter(products__contain=value),报错django.core.exceptions.FieldError: Unsupported lookup 'contain' for ForeignKey or join on the field not permitted.,推测是因为使用SQLite;
  • 使用Q对象和regex:queryset.filter(Q(data__regex=r'"name":.*"test"')),但始终返回空查询集,不过认可regex思路无需考虑data是否包含name键。

解决方案

方法1:利用Django JSON字段的专用Lookup(适配SQLite)

Django对JSONField提供了__contains、__icontains等查询方法,但需要注意关联查询的写法——必须通过products__data__前缀关联到Product表的JSON字段。

如果要搜索JSON中name键的值包含指定内容,代码修改如下:

def queryset(self, request, queryset):
    value = self.value()
    if value:
        # 匹配data中name键的值包含value,不区分大小写用__icontains
        return queryset.filter(products__data__name__contains=value)

注意:此方法要求SQLite版本≥3.31.0(Django 3.2+支持),Django会自动转换为SQLite兼容的JSON查询语法。

方法2:修正正则表达式的关联查询写法

之前的错误是直接调用data__regex,未关联到Product表,需加上products__前缀。同时SQLite存储JSON时可能存在空格差异,正则需兼容这种情况:

import re
from django.db.models import Q

def queryset(self, request, queryset):
    value = self.value()
    if value:
        # 兼容键值对之间的空格,用re.escape避免特殊字符破坏正则
        regex_pattern = r'"name"\s*:\s*".*{}.*"'.format(re.escape(value))
        return queryset.filter(products__data__regex=regex_pattern)

补充说明

  • 若SQLite版本过低不支持JSON函数,建议升级SQLite或切换到PostgreSQL,后者对JSON字段的查询支持更完善;
  • 若要实现全局模糊搜索(不指定具体键),可直接用products__data__icontains=value,匹配JSON字符串中是否包含指定文本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 07:43:22