web-dev-qa-db-ja.com

Django Rest Framework:ネストされたjsonフィールドからプレーンオブジェクトへのデータのシリアル化

非フラット構造を1つのフラットオブジェクトにシリアル化したい。受け取った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()
_

ご覧のとおり、モデルIssueのフィールドsummaryは、JSONデータとは異なり、idおよびkeyと同じオブジェクトにあります。

私のシリアライザは次です:

_    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にあります。私はこれらの方法を見ます:

  • それらを保持するためにもう1つのモデルFieldsを作成しますが、それはばかげています。私はそれを必要とせず、私のAPIは外部構造に厳密に依存しています。

  • .to_internal_fields()コンバーターをいくつか作成します。しかし、この場合、Issueのすべてのフィールドを手動で検証して準備し、何度も繰り返す必要があります。また、フィールドsummaryがシリアライザに参加していない場合、シリアライザはそれを使用できないか、検証が失敗します。

  • シリアライザ(コードでコメント化)にDictFieldを追加し、そこからデータを取得します。いいですか?それを検証する方法は?繰り返しますが、私はコードのきれいな構造を持っています。

次に、変更ログデータを解析して保存します。

そのような構造をよりうまく処理するにはどうすればよいですか?

ありがとうございました!

12
Eugene Lisitsky

最後に、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
    }
13
Eugene Lisitsky

データを受信するための独自のカスタムシリアライザーを作成することをお勧めします。あなたはそうすることができます:

_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()関数で必要に応じてデータを操作できます。ネストされたカスタムシリアライザーを作成して、このデータを解析することもできます。

1
djq

ドキュメンテーションは 直列化でのネスト を扱う上で重要な役割を果たします。

基本的に、次のようにネストされた値を持つ別のクラスを作成します。

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()
0
Danny Vu