简体   繁体   English

Django Rest Framework:将数据从嵌套的json字段序列化为普通对象

[英]Django Rest Framework: Serialize data from nested json fields to plain object

I want to serialize non-flat structure to a one flat object. 我想将非平面结构序列化为一个扁平对象。 Here's an example of an API call I receive (I cannot control it unfortunately): 这是我收到的API调用的一个例子(遗憾的是我无法控制它):

{
"webhookEvent": "jira:issue_updated",
"user": {
        "id": 2434,
        "name": "Ben",
    },
"issue": {
      "id": "33062",
      "key": "jira-project-key-111",
      "fields": {
          "summary": "The week ahead",
      },
"changelog": {
    "id": "219580",
    "items": [{
         "field": "status",
         "fieldtype": "jira",
         "from": "10127",
         "fromString": "Submitted",
         "to": "10128",
         "toString": "Staged"
    }]
},
"timestamp": 1423234723378
}

I'd like to serialize it to the models like these: 我想将它序列化为这样的模型:

class Issue(models.Model):
    jira_id = models.IntegerField()
    jira_id = models.CharField()
    summary = models.CharField()

class Change(models.Model):
    issue = models.ForeignKey(Issue)
    timestamp = models.DataTimeField()

As you can see, model Issue 's field summary is located on the same object as id and key unlike in JSON data. 如您所见,与JSON数据不同,模型Issue的字段summaryidkey位于同一对象上。

My Serializer are next: 我的Serializer是下一个:

    class ChangeSerializer(serializers.ModelSerializer):
        """Receives complex data from jira and converts into objects."""

        issue = JiraIssueSerializer()
        timestamp = TimestampField(source='created_at')

        class Meta:
            model = Change
            fields = ('issue', 'timestamp')

        def create(self, validated_data):
            super(serializers.ModelSerializer, self).create(validated_data=validated_data)
            jira_issue = JiraIssueSerializer(data=validated_data)
            issue = Issue.objects.get(jira_issue)
            self.created_at = datetime.utcnow()
            change = Change(**validated_data)
            return change



    class JiraIssueSerializer(serializers.ModelSerializer):
        """Issue serializer."""
        id = serializers.IntegerField(source='jira_id')
        key = serializers.CharField(source='jira_key')
        summary = serializers.CharField()   ### I want field to work!
        # fields = serializers.DictField(child=serializers.CharField())

        class Meta:
            model = Issue
            fields = ('id', 'key',
               'summary',
            )

        def to_internal_value(self, data):
           # ret = super(serializers.ModelSerializer,   self).to_internal_value(data)
           ret = {}
           # ret = super().to_internal_value(data)
           ret['jira_id'] = data.get('id', None)
           ret['jira_key'] = data.get('key', None)
           jira_issue_fields_data = data.get('fields')
           if jira_issue_fields_data or 1:
               summary = jira_issue_fields_data.get('summary', None)
               ret.update(summary=summary)
            print('to_internal_value', ret)
            return ret

         def to_representation(self, instance):
            ret = {}
            ret = super().to_representation(instance)
            fields = {}
            fields['summary'] = instance.summary
            ret.update(fields=fields)
            print(ret)
            return ret

I works well for fields in issue object in JSON. 我适用于JSON中的issue对象中的字段。 But how can I add to the JiraIssueSerializer some fields like summary ? 但是,我如何向JiraIssueSerializer添加一些像summary这样的字段? They are not direct fields of issue object, but located in substrcucture fields . 它们不是issue对象的直接字段,而是位于子结构fields I see these ways: 我看到这些方式:

  • Make one more Model Fields to keep them, but it's ridiculous. 再制作一个模型Fields来保留它们,但这很荒谬。 I do not need it and my API strictly depends on the foreign structure. 我不需要它,我的API严格依赖于外来结构。

  • Make some .to_internal_fields() convertors. 制作一些.to_internal_fields()转换器。 But in this case I have to manually validate and prepare all the fields in my Issue and repeat myself several times. 但在这种情况下,我必须手动验证并准备我的Issue所有字段,并重复几次。 Also If field summary is not enlisted in Serializer, latter cannot use it or validation fails. 此外,如果字段summary未在Serializer中登记,则后者无法使用它或验证失败。

  • Add DictField to Serializer (commented in code) and take data from it. DictField添加到Serializer(在代码中注释)并从中获取数据。 Is it good? 好吗? How to validate it? 如何验证? Again, I have n clean structure of code. 同样,我有n个干净的代码结构。

Next I'd like to parse and save changelog data. 接下来我想解析并保存更改日志数据。

How to deal better with such structures? 如何更好地处理这种结构?

Thank you! 谢谢!

Finally, solution was found in tests of django-rest-framework. 最后,在django-rest-framework的测试中发现了解决方案。
https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_serializer.py#L149 https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_serializer.py#L149

You may easily define nested serializers which will act as a containers and extract data to your plain object. 您可以轻松定义嵌套的序列化程序,它们将充当容器并将数据提取到普通对象。 Like so: 像这样:

    class NestedSerializer1(serializers.Serializer):
        a = serializers.IntegerField()
        b = serializers.IntegerField()

    class NestedSerializer2(serializers.Serializer):
        c = serializers.IntegerField()
        d = serializers.IntegerField()

    class TestSerializer(serializers.Serializer):
        nested1 = NestedSerializer1(source='*')
        nested2 = NestedSerializer2(source='*')

    data = {
        'nested1': {'a': 1, 'b': 2},
        'nested2': {'c': 3, 'd': 4}
     }

     serializer = TestSerializer(data=self.data)
     assert serializer.is_valid()

     assert serializer.validated_data == {
        'a': 1, 
        'b': 2,
        'c': 3, 
        'd': 4
    }

I would suggest you create your own custom serializer to receive the data. 我建议你创建自己的自定义序列化程序来接收数据。 You can do this like so: 你可以这样做:

from rest_framework import serializers

class MySerializer(serializers.Serializer):
    """
    Custom serializer
    """
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField()


    def create(self, validated_data):
        """Create a new object"""
        validated_data['custom_value'] = 0 # you can manipulate and restructure data here if you wish
        return MyModel.objects.create(**validated_data)

You can then manipulate the data as you wish in the create() function. 然后,您可以在create()函数中根据需要操作数据。 You could also create nested custom serializers to parse this data. 您还可以创建嵌套的自定义序列化程序来解析此数据。

Documentation has a good part of dealing with nesting in serialisation . 文档在处理序列化中的嵌套方面有很大的作用。

Basically, you want to create a separate class that has the nested values as follows: 基本上,您希望创建一个具有嵌套值的单独类,如下所示:

class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)

class CommentSerializer(serializers.Serializer):
    user = UserSerializer()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

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

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