使用Django模型将JSON数据写入关系数据库的最优雅方法是什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (329)

我在Django中有一个典型的关系数据库模型,其中典型的模型包含一些扩展Django的一些ForeignKeys,一些ManyToManyFields和一些字段DateTimeField

我想从外部API保存以JSON格式(而不是平面)接收的数据。我不希望它将数据保存到各个表(不是将整个json字符串保存到一个字段)。什么是最干净和简单的方法来做到这一点?有没有可用的库来使这个任务更简单?

这里有一个例子来澄清我的问题,

model-

class NinjaData(models.Model):
    id = models.IntegerField(primary_key=True, unique=True)
    name = models.CharField(max_length=60)  
    birthdatetime = MyDateTimeField(null=True)
    deathdatetime = MyDatetimeField(null=True)
    skills = models.ManyToManyField(Skills, null=True)
    weapons = models.ManyToManyField(Weapons, null=True)
    master = models.ForeignKey(Master, null=True)

class Skills(models.Model):
    id = models.IntegerField(primary_key=True, unique=True)
    name = models.CharField(max_length=60)
    difficulty = models.IntegerField(null=True)

class Weapons(models.Model):
    id = models.IntegerField(primary_key=True, unique=True)
    name = models.CharField(max_length=60)
    weight = models.FloatField(null=True)

class Master(models.Model):
    id = models.IntegerField(primary_key=True, unique=True)
    name = models.CharField(max_length=60)
    is_awesome = models.NullBooleanField()

现在,我通常必须将从外部api(secret ninja api)获得的json字符串数据保存到此模型中,json看起来像这样

JSON-

{
"id":"1234",
"name":"Hitori",
"birthdatetime":"11/05/1999 20:30:00",
"skills":[
    {
    "id":"3456",
    "name":"stealth",
    "difficulty":"2"
    },
    {
    "id":"678",
    "name":"karate",
    "difficulty":"1"
    }
],
"weapons":[
    {
    "id":"878",
    "name":"shuriken",
    "weight":"0.2"
    },
    {
    "id":"574",
    "name":"katana",
    "weight":"0.5"
    }
],
"master":{
    "id":"4",
    "name":"Schi fu",
    "is_awesome":"true"
    }
}

现在处理典型的ManyToManyField的逻辑非常简单,

逻辑代码 -

data = json.loads(ninja_json)
ninja = NinjaData.objects.create(id=data['id'], name=data['name'])

if 'weapons' in data:
    weapons = data['weapons']
    for weapon in weapons:
        w = Weapons.objects.get_or_create(**weapon)  # create a new weapon in Weapon table
        ninja.weapons.add(w)

if 'skills' in data:
    ...
    (skipping rest of the code for brevity)

有很多方法可以使用,

  • 上面的代码view完成了将json转换为模型实例的所有工作
  • 代码高于逻辑覆盖模型的__init__方法
  • 代码高于逻辑覆盖模型的save()方法
  • 创建一个管理器为每个模型和代码内每个方法这样的逻辑createget_or_createfilter等。
  • 延伸ManyToManyField并放在那里,
  • 一个外部库?

我想知道是否有一种最简单的方法可以将数据以这种json格式保存到数据库中,而无需多次编码上述逻辑,那么您会建议最优雅的方法是什么?

提问于
用户回答回答于

在我看来,你需要的代码最干净的地方是在NinjaData模型的自定义管理器上作为一个新的Manager方法(例如from_json_string)。

我认为你不应该重写标准的create,get_or_create等方法,因为你正在做一些与他们通常做的不同的事情,并且保持它们正常工作是很好的。

我意识到我可能会在某个时候想要这个,所以我已经编码并轻度测试了一个通用函数。由于它递归通过并影响其他模型,我不再确定它属于管理器方法,应该可能是一个独立的帮助函数。

def create_or_update_and_get(model_class, data):
    get_or_create_kwargs = {
        model_class._meta.pk.name: data.pop(model_class._meta.pk.name)
    }
    try:
        # get
        instance = model_class.objects.get(**get_or_create_kwargs)
    except model_class.DoesNotExist:
        # create
        instance = model_class(**get_or_create_kwargs)
    # update (or finish creating)
    for key,value in data.items():
        field = model_class._meta.get_field(key)
        if not field:
            continue
        if isinstance(field, models.ManyToManyField):
            # can't add m2m until parent is saved
            continue
        elif isinstance(field, models.ForeignKey) and hasattr(value, 'items'):
            rel_instance = create_or_update_and_get(field.rel.to, value)
            setattr(instance, key, rel_instance)
        else:
            setattr(instance, key, value)
    instance.save()
    # now add the m2m relations
    for field in model_class._meta.many_to_many:
        if field.name in data and hasattr(data[field.name], 'append'):
            for obj in data[field.name]:
                rel_instance = create_or_update_and_get(field.rel.to, obj)
                getattr(instance, field.name).add(rel_instance)
    return instance

# for example:
from django.utils.simplejson import simplejson as json

data = json.loads(ninja_json)
ninja = create_or_update_and_get(NinjaData, data)
用户回答回答于

我不知道你是否熟悉术语,但你基本上想要做的是从序列化/字符串格式(在这种情况下,JSON)反序列化为Python模型对象。

我不熟悉用JSON做这件事的Python库,所以我不能推荐/支持任何,但使用诸如“python”,“deserialization”,“json”,“object”和“graph”之类的术语进行搜索。似乎揭示了一些用于序列化的Django文档和github上的库jsonpickle

扫码关注云+社区