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

Posted

技术标签:

【中文标题】Django Rest Framework:将嵌套 json 字段中的数据序列化为普通对象【英文标题】:Django Rest Framework: Serialize data from nested json fields to plain object 【发布时间】:2016-09-16 09:35:47 【问题描述】:

我想将非平面结构序列化为一个平面对象。 这是我收到的 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

我想把它序列化成这样的模型:

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

如您所见,与 JSON 数据不同,模型 Issue 的字段 summaryidkey 位于同一对象上。

我的序列化器是下一个:

    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

我适用于 JSON 中 issue 对象中的字段。 但是如何向 JiraIssueSerializer 添加一些字段,例如 summary?它们不是issue 对象的直接字段,而是位于子结构fields 中。 我看到了这些方式:

再制作一个模型Fields 来保留它们,但这很荒谬。我不需要它,我的 API 严格依赖于外部结构。

制作一些.to_internal_fields() 转换器。但在这种情况下,我必须手动验证和准备我的Issue 中的所有字段,并重复自己几次。此外,如果字段summary 未在序列化程序中登记,则后者无法使用它或验证失败。

DictField 添加到序列化器(在代码中注释)并从中获取数据。好吗?如何验证它?同样,我有干净的代码结构。

接下来我想解析并保存变更日志数据。

如何更好地处理这样的结构?

谢谢!

【问题讨论】:

【参考方案1】:

文档在处理nesting in serialisation 方面发挥了重要作用。

基本上,您希望创建一个具有嵌套值的单独类,如下所示:

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

【讨论】:

【参考方案2】:

终于在django-rest-framework的测试中找到了解决方案。https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_serializer.py#L149

您可以轻松定义嵌套序列化程序,它们将充当容器并将数据提取到您的普通对象。像这样:

    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
    

【讨论】:

您能详细解释一下您是如何解决这个问题的吗?谢谢! @TonyWang,我创建了 3 个序列化程序:***的一个 TestSerializer,以及 2 个嵌套的 NestedSerializer1NestedSerializer2。然后在顶层我设置source="*",这样所有嵌套字段都将传播到父序列化程序。因此,您可能有一个字典 "a":1, "b":2, "c":3,"d":4,它将被序列化为具有 2 个字段 nested1nested2 的 json 对象。第一个包含字段ab。第二个 - cd。具有适当的价值观。结果:'nested1': 'a': 1, 'b': 2, 'nested2': 'c': 3, 'd': 4【参考方案3】:

我建议您创建自己的自定义序列化程序来接收数据。你可以这样做:

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)

然后您可以在create() 函数中随意操作数据。您还可以创建嵌套的自定义序列化程序来解析这些数据。

【讨论】:

不,它会为你验证它 我已经找到了绕过它的方法。这是一个标准的计划方式!

以上是关于Django Rest Framework:将嵌套 json 字段中的数据序列化为普通对象的主要内容,如果未能解决你的问题,请参考以下文章

django REST framework 嵌套序列化器和 POST 嵌套 JSON 文件

Django REST Framework 序列化程序中的嵌套注释字段

django-rest-framework、多表模型继承、ModelSerializers 和嵌套序列化器

python django-rest-framework 3.3.3 更新嵌套序列化程序

Django Rest Framework,如何更新序列化程序中的嵌套值

使用 Django 的 ORM 和 Django Rest Framework 序列化嵌套关系的查询集的正确方法?