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

Django中如何按JSONField多值过滤关联模型并实现库存聚合?

Django模型多值属性过滤与EAV模型选型问题

现有模型结构

class ItemCategory(models.Model):
    name = CharField()

class Item(models.Model):
    brand = CharField()
    model = CharField()
    category = ForeignKey(ItemCategory)
    attributes = JSONField(default=dict) # 示例:{"size": 1000, "power": 250}

class StockItem(models.Model):
    item = ForeignKey(Item)
    stock = ForeignKey(Stock)
    quantity = PositiveIntegerField()

class Container(models.Model):
    name = CharField()

class ContainerItem(models.Model):
    container = models.ForeignKey(Container)
    item_category = models.ForeignKey(ItemCategory)
    attributes = models.JSONField(default=dict)
    quantity = models.PositiveIntegerField()

需求说明

当前ContainerListView已支持基于ContainerItem的单值attributes(如{"size": 1000})过滤Item并计算库存潜力,现需扩展支持ContainerItem的attributes中key对应多值(如"size": [1000, 800]),筛选出Item的attributes中对应key的值属于该列表的记录,同时咨询是否采用EAV模型是更优设计。


一、多值属性过滤的实现方案

1. Django ORM原生方案(避免RawSQL)

利用Django JSONField的查询API,通过循环构建查询条件,适配单值/多值两种场景:

from django.db.models import Q

def get_matching_items(container):
    container_items = container.containeritem_set.all()
    query_conditions = Q()
    
    for ci in container_items:
        for attr_key, attr_value in ci.attributes.items():
            if isinstance(attr_value, list):
                # 多值匹配:Item的对应属性值在列表内
                query_conditions |= Q(**{f"attributes__{attr_key}__in": attr_value})
            else:
                # 单值匹配(原有逻辑)
                query_conditions |= Q(**{f"attributes__{attr_key}": attr_value})
    
    # 同时过滤所属分类匹配的Item
    valid_categories = container_items.values_list("item_category_id", flat=True)
    return Item.objects.filter(query_conditions).filter(category_id__in=valid_categories)

2. 数据库层面优化方案(RawSQL)

如果ContainerItem数量较多,循环构建条件效率较低,可直接用PostgreSQL的JSON函数实现批量匹配(适用于使用PostgreSQL的场景):

from django.db.models import RawSQL

def get_matching_items_raw(container):
    return Item.objects.raw('''
        SELECT DISTINCT i.*
        FROM myapp_item i
        JOIN myapp_containeritem ci ON i.category_id = ci.item_category_id
        WHERE ci.container_id = %s
          AND EXISTS (
              SELECT 1
              FROM jsonb_each(ci.attributes) AS attr(key, value)
              WHERE 
                  CASE 
                      WHEN jsonb_typeof(value) = 'array' THEN
                          i.attributes ->> attr.key = ANY(value::text[])
                      ELSE
                          i.attributes ->> attr.key = value::text
                  END
          )
    ''', [container.id])

二、EAV模型的选型分析

EAV模型的优势

  • 结构化存储:属性与值分开存储,支持SQL原生索引、类型约束,查询性能更稳定,适合高频属性查询场景
  • 类型可控:可针对不同属性类型(整数、字符串、布尔等)设计字段,避免JSONField的类型模糊问题
  • 扩展性强:便于后续添加属性统计、跨属性分析等复杂需求

EAV模型的劣势

  • 模型复杂度提升:需新增Attribute(属性定义)、ItemAttribute(Item与属性的关联)等表,增加数据库维护成本
  • 查询复杂度高:简单查询也需要多表关联,代码编写更繁琐
  • 写入效率低:新增/更新Item属性时,需操作多个关联表,步骤更繁琐

选型建议

  • 如果你的业务中属性查询非常频繁,需要严格的类型校验、索引优化,或未来有大量属性统计分析需求,EAV模型更优
  • 如果属性结构灵活多变,大部分查询为简单匹配,且不想增加模型复杂度,继续使用JSONField+优化后的查询方案更合适——当前仅需支持多值匹配,通过上述ORM或RawSQL方案即可解决,无需重构为EAV模型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 03:15:22