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

Django模型JSONField查询与is_returned字段更新方法求助

问题:Django JSONField中更新ordered_items内is_returned字段值失败

我正在开发一个使用Django JSONField存储实例JSON数据的项目,按照官方文档查询对象时未达预期,无法查询并更新JSONField内is_returned键的值。以下是相关详情:

模型代码

class PosOrder(models.Model):
    store = models.ForeignKey('store.Store', on_delete=models.CASCADE, null=True)
    order_number = models.CharField(max_length=30, null=True)
    customer_phone = models.CharField(max_length=100, null=True, blank=True)
    order_date = models.DateTimeField(auto_now_add=True)
    order_total = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    order_discount = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    order_tax = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    coupon = models.ForeignKey('shoppingcoupon.ShoppingCoupon', on_delete=models.PROTECT, null=True, blank=True)
    order_grand_total = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    payment_method = models.CharField(max_length=100, null=True, blank=True, choices=(
        ('cash', 'Cash'), ('card', 'Card'), ('online', 'Online')))
    order_status = models.CharField(max_length=100, null=True, blank=True, choices=order_status)
    payment_status = models.CharField(max_length=100, null=True, blank=True, choices=payment_status)
    payment_id = models.CharField(max_length=100, null=True, blank=True)
    complete = models.BooleanField(default=False)
    order_data = models.JSONField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey('hrms.Employee', on_delete=models.CASCADE, null=True, blank=True)

JSONField示例数据

{
    "id": 408,
    "store": {"id": 1, "name": "Salt Lake"},
    "order_number": "POS408",
    "customer_phone": "9877454577",
    "order_status": "processed",
    "payment_status": null,
    "payment_method": "cash",
    "created_at": "2022-08-09T11:38:37.207775+05:30",
    "created_by": {"id": 5, "code": "RMS003"},
    "get_order_total": 39206,
    "get_order_total_discount_amount": 2232,
    "get_order_total_without_tax": 32730,
    "get_order_total_tax": 6476,
    "ordered_items": [
        {
            "id": 41,
            "item": {
                "id": 3,
                "product": {
                    "id": 3,
                    "sku": "TM002",
                    "name": "Striped Men Polo Neck White, Blue, Yellow T-Shirt",
                    "barcode": "552123",
                    "manufacturer": null,
                    "created_at": "2022-08-06T15:04:05.509651+05:30",
                    "category": 1,
                    "brand": null,
                    "size": null,
                    "color": null,
                    "vendor": null,
                    "created_by": 9
                },
                "mrp": "1599.00",
                "price_without_tax": "1427.68",
                "tax": 12,
                "tax_amount": "154.19",
                "discount_percentage": "10.00",
                "discount_amount": "142.77",
                "price": "1439.10",
                "stock": 100,
                "created_at": "2022-08-07T17:13:33.640401+05:30",
                "updated_at": "2022-08-07T17:13:33.639749+05:30",
                "store": 1,
                "created_by": 9,
                "offers": []
            },
            "quantity": 2,
            "mrp": "1599.00",
            "price": "1439.10",
            "discount": "319.80",
            "tax": "22.84",
            "mrp_total": "3198.00",
            "total_without_tax": "2855.36",
            "total": "2878.20",
            "is_returned": false,
            "is_exchanged": false,
            "order": 408,
            "offer": null
        },
        {
            "id": 42,
            "item": {
                "id": 2,
                "product": {
                    "id": 2,
                    "sku": "TFM01",
                    "name": "Demo 1",
                    "barcode": "012345",
                    "manufacturer": null,
                    "created_at": "2022-07-29T14:32:14.900200+05:30",
                    "category": 1,
                    "brand": 1,
                    "size": 1,
                    "color": 1,
                    "vendor": null,
                    "created_by": null
                },
                "mrp": "38240.00",
                "price_without_tax": "29875.00",
                "tax": 28,
                "tax_amount": "7946.75",
                "discount_percentage": "5.00",
                "discount_amount": "1493.75",
                "price": "36328.00",
                "stock": 100,
                "created_at": "2022-08-06T12:15:45.279121+05:30",
                "updated_at": "2022-08-06T12:15:38+05:30",
                "store": 1,
                "created_by": 1,
                "offers": [1, 2]
            },
            "quantity": 1,
            "mrp": "38240.00",
            "price": "36328.00",
            "discount": "1912.00",
            "tax": "6453.00",
            "mrp_total": "38240.00",
            "total_without_tax": "29875.00",
            "total": "36328.00",
            "is_returned": false,
            "is_exchanged": false,
            "order": 408,
            "offer": null
        }
    ]
}

需求

更新JSONField中ordered_items数组内指定项的is_returned键值。


解决方案

方式一:取出对象修改后保存(简单场景适用)

如果只需更新单个订单内的指定商品,先查询出PosOrder对象,修改order_data中的对应字段后保存:

# 示例:更新订单号POS408中,ordered_items里id为41的商品is_returned为True
order = PosOrder.objects.get(order_number="POS408")
for item in order.order_data["ordered_items"]:
    if item["id"] == 41:
        item["is_returned"] = True
        break
order.save()

这种方式直观易懂,但数据量大时效率较低,需将整个JSON数据拉取到内存修改后再写入数据库。

方式二:使用Django JSON路径表达式直接更新(高效批量操作)

Django 3.1及以上版本支持JSON路径查询与更新,可直接通过数据库操作修改JSON内字段,无需拉取整个对象:

1. 更新指定订单内的特定商品

from django.db.models import F
from django.db.models.functions import JSONObject

PosOrder.objects.filter(
    order_number="POS408",
    order_data__ordered_items__contains=[{"id": 41}]
).update(
    order_data=F('order_data').transform(
        lambda obj: [
            JSONObject(**{**item, "is_returned": True}) if item["id"] == 41 else item
            for item in obj["ordered_items"]
        ],
        path=["ordered_items"]
    )
)

2. 批量更新符合条件的商品

from django.db.models import F
from django.db.models.functions import JSONObject

# 示例:将所有ordered_items中商品sku为TM002的is_returned设为True
PosOrder.objects.filter(
    order_data__ordered_items__item__product__sku="TM002"
).update(
    order_data=F('order_data').transform(
        lambda obj: [
            JSONObject(**{**item, "is_returned": True}) if item["item"]["product"]["sku"] == "TM002" else item
            for item in obj["ordered_items"]
        ],
        path=["ordered_items"]
    )
)

注意事项

  • 需确保Django版本≥3.1,低版本需升级或使用方式一;
  • transform方法依赖数据库的JSON函数(如PostgreSQL的jsonb_set),在数据库层面执行修改,比拉取对象更高效;
  • 查询条件中__contains用于匹配数组中的对象,__可深入JSON层级访问嵌套字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 22:36:20