简体   繁体   English

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

[英]How to deserialize nested related JSON Data with django-rest-framework?

Here are some simplified Code-snippets for my Problem: 这是我的问题的一些简化代码片段:

Django Model: 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)

Serializers: 序列化器:

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 data to be deserialized: 要反序列化的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."
    }
}

Note, that i have no influence on how the JSON is structured since i get this from the game api from the popular online game "League of legends". 请注意,我对JSON的结构没有任何影响,因为我是从流行的在线游戏“英雄联盟”的游戏api中获得的。 I simplified it a lot for this example, there are a lot more fields and additional levels of depth. 在此示例中,我对其进行了很多简化,其中包含更多字段和更多层次的深度。

I started deserializing only the Champion fields on the top level which worked fine. 我开始只反序列化顶级的Champion字段,效果很好。 Than i added The ChampionSpells and ChampionPassive without the deeper level things inside these thick also worked fine. 比起我添加的ChampionSpells和ChampionPassive,如果没有这些较深层次的东西,它们也可以正常工作。

When i added the 2nd depth level with including the SpellImageInfo into the ChampionSpell deserialization i got the following error: 当我添加第二个深度级别并将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

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

The execution of the is_valid() method returns True but when i call the save() method of the ChampionSerializer, i get this error. is_valid()方法的执行返回True但是当我调用ChampionSerializer的save()方法时,出现此错误。 I couldn't figure out why, because image is definitely a field of the ChampionSpell Model through the ForeignKey inside SpellImageInfo . 我不知道为什么,因为通过SpellImageInfo内部的ForeignKey, image绝对是ChampionSpell模型的一个字段。 Also for one level of depth less (Leaving the SpellImageInfo out) it worked fine. 同样,对于少一层的深度(不包括SpellImageInfo),它也可以正常工作。

Has anybody a solution or an explanation for this? 有没有人对此有解决方案或解释?

I am not certain this is exactly what you are after, but maybe it is a start in the right direction for you at least: 我不确定这正是您所追求的,但是至少对于您来说,这可能是正确方向的起点:

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()

Output: 输出:

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.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM