Django + Postgres:将 JSON 字符串作为 JSON 类型直接保存到模型中

gli*_*its 4 python django postgresql json pandas

我有一个 Django 模型,我想在其中保存无模式的键值元数据,所以我使用django.contrib.postgres.fields.JSONField.

键值数据是 Pandas 系列,默认情况下它不是 JSON 可序列化的(由于 numpy.int64、numpy.float64 类型),所以我使用了 handy series.to_json(),它返回一个已经序列化的 JSON 字符串。

为了将它保存到我的模型中,我运行json.loads了它。但我知道json.dumps当数据保存到 Postgres 时会调用它。

如何避免这种不必要的反序列化/重新序列化步骤?

代码示例:

def create_model(pandas_series):
    mdl = Model()
    metadata = pandas_series.to_json()  # gives a JSON string
    mdl.metadata = json.loads(metadata) # string->dict, then dict->string
    mdl.save()
Run Code Online (Sandbox Code Playgroud)

e4c*_*4c5 8

瓶颈在哪里

我以编程方式创建了一个包含数千个元素的字典,它看起来像这样:

d = { ... , "a240": 240, "a243": 243, "a242", ...}

然后我json.loads(json.dumps(d))循环 1000 次。1000 次迭代平均需要 510 毫秒。

然后我创建了以下模型:

class JsonModel(models.Model):
    jfield = JSONField()
Run Code Online (Sandbox Code Playgroud)

将字典插入数据库1000次如下:

JsonModel.objects.create(jfield = d)
Run Code Online (Sandbox Code Playgroud)

此操作平均耗时 21 秒。所以看起来真正的瓶颈在于将数据保存到数据库而不是序列化反序列化步骤。

所以总而言之,保存到数据库大约比 json 转储/加载慢 40 倍(但您的里程将根据字典的大小、数据库上的负载等而有所不同)。

避免额外的序列化/反序列化步骤

根据上述数字不转储/加载可能不值得。尽管如此,如果您想尝试一下,要做的就是创建一个自定义字段。它可以像下面这样简单:

from django.contrib.postgres.fields import JSONField
class MyJsonField(JSONField):
    def get_prep_value(self, value):
        if isinstance(value, str):
            return value
        return super(MyJsonField,self).get_prep_value(value)
Run Code Online (Sandbox Code Playgroud)

那么你的模型可以是

class JsonModel(models.Model):
    metadata = MyJsonField()
    ....
Run Code Online (Sandbox Code Playgroud)

然后在创建db记录的时候,可以传入json序列化的sting而不是调用json.loads

mdl.metadata = metadata # no string->dict, then dict->string
Run Code Online (Sandbox Code Playgroud)