如何使用django-rest-framework反序列化嵌套的相关JSON数据?

时间:2016-04-01 20:38:40

标签: python json django serialization django-rest-framework

以下是我的问题的一些简化代码片段:

Django模型:

class Champion(models.Model):
    id = models.PositiveIntegerField(primary_key=True)
    name = models.CharField(max_length=30)  
    # spells ([List] through ForeignKey in ChampionSpells)
    # passive (through ForeignKey in ChampionPassive)

    def __str__(self):
        return self.name

class ChampionPassive(models.Model):
    champion = models.ForeignKey(Champion, related_name='passive', related_query_name='passive')
    description = models.TextField()
    name = models.CharField(max_length=30)  

class ChampionSpell(models.Model):
    champion = models.ForeignKey(Champion, related_name='spells',     related_query_name='spell')
    cooldownBurn = models.CharField(max_length=40)  
    costBurn = models.CharField(max_length=40)  
    costType = models.CharField(max_length=10)
    # image (through ForeignKey in ChampionImageInfo)

class SpellImageInfo(models.Model):
    spell = models.ForeignKey(ChampionSpell, related_name='image', related_query_name='image')
    full = models.CharField(max_length=200)
    group = models.CharField(max_length=200)

串行器:

class ChampionPassiveSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChampionPassive
        exclude = ('champion',)

class SpellImageInfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = SpellImageInfo
        exclude = ('spell',)

class ChampionSpellSerializer(serializers.ModelSerializer):
    # after adding this field, i get a TypeError
    image = SpellImageInfoSerializer()

    class Meta:
        model = ChampionSpell
        exclude = ('champion',)

 class ChampionSerializer(serializers.ModelSerializer):
    passive = ChampionPassiveSerializer()
    spells = ChampionSpellSerializer(many=True)

    class Meta:
        model = Champion

    def create(self, validated_data):
        spells_data = validated_data.pop('spells')
        passive_data = validated_data.pop('passive')
        champion = Champion.objects.create(**validated_data)
        for spell_data in spells_data:
            spell = ChampionSpell.objects.create(champion=champion, **spell_data)
            spell_image_data = spell_data.pop('image')
            SpellImageInfo.objects.create(spell=spell, **spell_image_data)
        ChampionPassive.objects.create(champion=champion, **passive_data)
        return champion

要反序列化的JSON数据:

{
    "id": 1,
    "name": "Annie",
    "spells": [{
        "name": "Disintegrate",
        "description": "Annie hurls a Mana infused fireball, dealing damage and refunding the Mana cost if it destroys the target.",
        "image": {
            "full": "Disintegrate.png",
            "group": "spell"
        },
        "cooldownBurn": "4",
        "costType": "Mana",
        "costBurn": "60\/65\/70\/75\/80"
    }, {
        "name": "Incinerate",
        "description": "Annie casts a blazing cone of fire, dealing damage to all enemies in the area.",
        "image": {
            "full": "Incinerate.png",
            "group": "spell"
        },
        "cooldownBurn": "8",
        "costType": "Mana",
        "costBurn": "70\/80\/90\/100\/110"
    }],
    "passive": {
        "name": "Pyromania",
        "description": "After casting 4 spells, Annie's next offensive spell will stun the target for a short duration."
    }
}

请注意,我对JSON的结构没有任何影响,因为我从流行的在线游戏“英雄联盟”的游戏api中得到了这个结构。我为这个例子简化了很多,还有更多的字段和更多的深度。

我开始只对最高级别的Champion字段进行反序列化,效果很好。比起我添加了ChampionSpells和ChampionPassive而没有更深层次的东西,这些厚实的内容也很好。

当我添加第二个深度级别并将SpellImageInfo包含在ChampionSpell反序列化中时,我收到以下错误:

----------------------------------------------------------------------
  File "/home/ubuntu/workspace/lolstatistics/stats/serializers.py",     line 111, in create
    spell = ChampionSpell.objects.create(champion=champion, **spell_data)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 399, in create
    obj = self.model(**kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 443, in __init__
    raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
TypeError: 'image' is an invalid keyword argument for this function

----------------------------------------------------------------------

is_valid()方法的执行返回True但是当我调用ChampionSerializer的save()方法时,我收到此错误。我无法弄清楚原因,因为image绝对是ChampionSpell模型中通过SpellImageInfo内的ForeignKey的字段。此外,对于一个深度较少的水平(离开SpellImageInfo),它工作正常。

有人对此有解决方案或解释吗?

1 个答案:

答案 0 :(得分:0)

我不确定这正是你所追求的,但也许它至少是你正确方向的开始:

import json

j = """{
    "id": 1,
    "name": "Annie",
    "spells": [{
        "name": "Disintegrate",
        "description": "Annie hurls a Mana infused fireball, dealing damage and refunding the Mana cost if it destroys the target.",
        "image": {
            "full": "Disintegrate.png",
            "group": "spell"
        },
        "cooldownBurn": "4",
        "costType": "Mana",
        "costBurn": "60\/65\/70\/75\/80"
    }, {
        "name": "Incinerate",
        "description": "Annie casts a blazing cone of fire, dealing damage to all enemies in the area.",
        "image": {
            "full": "Incinerate.png",
            "group": "spell"
        },
        "cooldownBurn": "8",
        "costType": "Mana",
        "costBurn": "70\/80\/90\/100\/110"
    }],
    "passive": {
        "name": "Pyromania",
        "description": "After casting 4 spells, Annie's next offensive spell will stun the target for a short duration."
    }
}"""

class DeserializeJSON(object):
    def __init__(self, j):
        self.__dict__ = json.loads(j)

d = DeserializeJSON(j)

print(d.id)
print(d.name)
print()
print(d.spells[0]["name"])
print()
print(d.spells[0]["image"]["full"])
print()
print(d.passive["name"])
print()

for k, v in enumerate(d.spells):
    print(k)
    print(v["name"])
    print(v["description"])
    print()

输出:

1
Annie

Disintegrate

Disintegrate.png

Pyromania

0
Disintegrate
Annie hurls a Mana infused fireball, dealing damage and refunding the Mana cost if it destroys the target.

1
Incinerate
Annie casts a blazing cone of fire, dealing damage to all enemies in the area.