透明地将Django模型字段存储为JSON数据

2 投票
1 回答
1908 浏览
提问于 2025-04-18 05:00

假设我有一个对象,叫做“订单”,它里面有一个字段“items”,这个字段存储了一系列的订单项。因为这些订单项在数据库中不会单独被搜索或选择,所以我只想把它作为一个JSON字符串存储在数据库的一个字段里。

我在想怎么把这个功能嵌入到模型中,让使用这个模型的人感觉不到太多复杂性。我觉得保存模型其实挺简单的——只需要重写保存方法,把“items”列表转成一个内部的“_items”字段,然后把它写入数据库。不过,我对如何把它转回去有点困惑。我考虑过用类方法来创建,或者创建一个自定义的管理器,或者用信号之类的,但搞得我自己很迷糊。我相信这个问题已经被解决过很多次了,我很好奇大家认为的最佳做法是什么。

示例类:

class OrderItem():
    def __init__(self, desc="", qty=0):
        self.desc = desc
        self.qty = qty


class Order(Model):
    user = ForeignKey(User)
    _items = TextField() 

    def save(self, *args, **kwargs):
        self._items = jsonpickle.encode(self.items)
        super(Order, self).save(*args, **kwargs)

示例用法:

order = Order()
order.items = [OrderItem("widget", 5)]
order.save()

这样会在数据库中创建一条记录,其中

_items = [{"desc":"widget", "qty":5}]

现在我想在后面能够选择这个对象

order = Order.objects.get(id=whatever)

并且让订单的items变成解开的订单项数组,而不是存储的JSON字符串。

编辑:

解决方案其实很简单,我在这里分享一下,希望能帮助到其他新手。根据Daniel的建议,我选择了这个自定义模型字段:

class JSONField(with_metaclass(SubfieldBase, TextField)):
    def db_type(self, connection):
        return 'JSONField'

    def to_python(self, value):
        if isinstance(value, basestring):
            return jsonpickle.decode(value)
        else:
            return value

    def get_prep_value(self, value):
        return jsonpickle.encode(value)

1 个回答

1

一种更好的方法是创建一个新的类,继承自文本字段(TextField),并重写一些相关的方法,这样就可以在需要的时候自动处理数据的保存和读取。实际上,已经有很多这样的实现了,比如这里就有一个

撰写回答