非フラット構造を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
を追加し、そこからデータを取得します。いいですか?それを検証する方法は?繰り返しますが、私はコードのきれいな構造を持っています。
次に、変更ログデータを解析して保存します。
そのような構造をよりうまく処理するにはどうすればよいですか?
ありがとうございました!
最後に、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
}
データを受信するための独自のカスタムシリアライザーを作成することをお勧めします。あなたはそうすることができます:
_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()
関数で必要に応じてデータを操作できます。ネストされたカスタムシリアライザーを作成して、このデータを解析することもできます。
ドキュメンテーションは 直列化でのネスト を扱う上で重要な役割を果たします。
基本的に、次のようにネストされた値を持つ別のクラスを作成します。
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()