validated_data 使用 Django Rest Framework 为嵌套字段返回空的 OrderedDict
validated_data is returning empty OrderedDict for nested fields with Django Rest Framework
谁能帮我弄清楚为什么使用 Django 和 Django-rest-framework 的嵌套序列化程序无法正确解析某些字段?
我已经在 SO 上研究了这个问题,我发现发生这种情况的唯一原因是请求是作为表单数据而不是 Json 发送的,但我已经检查过 response.content_type 等于 application/json - 所以这不应该是这里的问题。
这就是我的 validated_data 的样子(注意其中 3 个字段只包含一个空的 OrderedDict):
{'author': OrderedDict(),
'disclosed_at': datetime.datetime(2021, 10, 19, 12, 0, tzinfo=<DstTzInfo 'Europe/Stockholm' CEST+2:00:00 DST>),
'event_date': datetime.date(2021, 10, 20),
'event_type': OrderedDict(),
'subject_companies': [OrderedDict()]}
这就是 request.data 的样子(您可以看到所有字段都在那里,并且具有每个序列化程序指定的字段,在本段下方提供):
{'event_type': {'pk': 1}, 'author': {'pk': 1}, 'event_date': '2021-10-20', 'disclosed_at': '2021-10-19 12:00:00', 'subject_companies': [{'pk': 1}]}
这是我发送请求的地方 (test.py):
def test_authenticated_creating_event_for_own_organisation(self):
view = NewsEventList.as_view()
url = reverse('news_event_list', kwargs={'pk': self.organisation.pk})
request = self.client.post(url, data=json.dumps(self.payload_event), content_type='application/json')
force_authenticate(request, user=self.user)
response = view(request, pk=self.organisation.pk)
json_data = json.dumps(response.data, indent=4)
json_ = (json.loads(json_data))
self.assertEqual(response.status_code, 201, 'Should return 201 - Created')
return response
型号
class NewsEvent(TimeStampedModel):
event_type = models.ForeignKey('publication.eventtype', on_delete=models.SET_NULL, related_name='events_type', null=True)
author = models.ForeignKey('core.organisation', on_delete=models.CASCADE, related_name='events_author', null=True)
subject_companies = models.ManyToManyField('core.organisation', related_name='events_companies')
legacy_id = models.CharField(max_length=128, blank=True)
event_date = models.DateField(null=True, blank=True)
event_time = models.TimeField(null=True, blank=True)
disclosed_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return '{}: {}'.format(self.author, self.event_type)
class Meta:
ordering = ('pk',)
class EventType(models.Model):
language = models.ForeignKey(
'core.Language',
default=get_default_language,
null=True,
on_delete=models.SET_NULL,
related_name='event_contents'
)
name = models.CharField(max_length=64, default=None)
key = models.CharField(max_length=64, default=None)
def __str__(self):
return '{}'.format(self.name)
class Meta:
ordering = ('pk',)
景色
class NewsEventList(generics.ListCreateAPIView):
permission_classes = (IsAuthenticatedAndOfSameOrganisationEvents,)
serializer_class = NewsEventSerializer
def get_queryset(self):
org_pk = self.kwargs.get('pk', None)
try:
org_obj = Organisation.objects.get(pk=org_pk)
except Organisation.DoesNotExist:
return ValidationError('Organisation does not exist')
news_events = NewsEvent.objects.filter(author=org_obj)
return news_events
序列化器
class OrganisationNameSerializer(ModelSerializer):
class Meta:
model = Organisation
fields = ['pk']
class EventTypeSerializer(ModelSerializer):
class Meta:
model = EventType
fields = ['pk']
class HeadlineSerializer(ModelSerializer):
class Meta:
model = EventHeadline
fields = ['news_event', 'language', 'headline']
class NewsEventSerializer(ModelSerializer):
event_type = EventTypeSerializer()
author = OrganisationNameSerializer()
subject_companies = OrganisationNameSerializer(many=True)
class Meta:
model = NewsEvent
fields = ['pk', 'event_type', 'author', 'event_date', 'event_time', 'disclosed_at', 'subject_companies', 'created_at', 'updated_at']
def create(self, validated_data):
# Get PK for organisation from URL
org_pk = self.context.get('request').parser_context.get('kwargs', {}).get('pk', {})
org_obj = Organisation.objects.get(pk=org_pk)
print(self.context.get('request').data)
pprint(validated_data)
另外,为了参考,我为一个已经存在的 NewsEvent 实例打印了 serializer.data:
news_event_test = NewsEvent.objects.all()[0]
serializer = NewsEventSerializer(news_event_test)
print(serializer.data)
{'pk': 1, 'event_type': OrderedDict([('pk', 1)]), 'author': OrderedDict([('pk', 1)]), 'event_date': None, 'event_time': None, 'disclosed_at': None, 'subject_companies': [OrderedDict([('pk', 1)])], 'created_at': '2021-10-25T09:32:41.562428+02:00', 'updated_at': '2021-10-25T09:32:41.562487+02:00'}
我也尝试过对 validated_object 中的每个字段进行“弹出”,但只有那些不是空的 OrderedDict 工作,例如 disclosed_at,但如果我尝试这样做:
event_type = validated_data.pop('event_type')
我得到:
KeyError: "Got KeyError when attempting to get a value for field `event_type` on serializer `NewsEventSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'event_type'."
event_type = models.ForeignKey('publication.EventType', on_delete=models.SET_NULL, related_name='events_type', null=True)
author = models.ForeignKey('core.Organisation', on_delete=models.CASCADE, related_name='events_author', null=True)
subject_companies = models.ManyToManyField('core.Organisation', related_name='events_companies')
替换那些行并删除以前的迁移并创建新的并尝试。
原因是我正在执行写入操作(POST 请求,以 Django/DRF 术语创建操作)并且序列化程序上的 pk 字段默认为 read_only。
要在写入操作期间验证该数据,您需要明确设置该字段并设置 read_only=False。例如:
class RedditTestSerializer(serializers.ModelSerializer):
class Meta:
model = models.Task
fields = ("pk",)
这解决了问题,但您可能需要考虑使用
PrimaryKeyRelatedField 如果您的用例仅设置主键
相关对象的。
谁能帮我弄清楚为什么使用 Django 和 Django-rest-framework 的嵌套序列化程序无法正确解析某些字段?
我已经在 SO 上研究了这个问题,我发现发生这种情况的唯一原因是请求是作为表单数据而不是 Json 发送的,但我已经检查过 response.content_type 等于 application/json - 所以这不应该是这里的问题。
这就是我的 validated_data 的样子(注意其中 3 个字段只包含一个空的 OrderedDict):
{'author': OrderedDict(),
'disclosed_at': datetime.datetime(2021, 10, 19, 12, 0, tzinfo=<DstTzInfo 'Europe/Stockholm' CEST+2:00:00 DST>),
'event_date': datetime.date(2021, 10, 20),
'event_type': OrderedDict(),
'subject_companies': [OrderedDict()]}
这就是 request.data 的样子(您可以看到所有字段都在那里,并且具有每个序列化程序指定的字段,在本段下方提供):
{'event_type': {'pk': 1}, 'author': {'pk': 1}, 'event_date': '2021-10-20', 'disclosed_at': '2021-10-19 12:00:00', 'subject_companies': [{'pk': 1}]}
这是我发送请求的地方 (test.py):
def test_authenticated_creating_event_for_own_organisation(self):
view = NewsEventList.as_view()
url = reverse('news_event_list', kwargs={'pk': self.organisation.pk})
request = self.client.post(url, data=json.dumps(self.payload_event), content_type='application/json')
force_authenticate(request, user=self.user)
response = view(request, pk=self.organisation.pk)
json_data = json.dumps(response.data, indent=4)
json_ = (json.loads(json_data))
self.assertEqual(response.status_code, 201, 'Should return 201 - Created')
return response
型号
class NewsEvent(TimeStampedModel):
event_type = models.ForeignKey('publication.eventtype', on_delete=models.SET_NULL, related_name='events_type', null=True)
author = models.ForeignKey('core.organisation', on_delete=models.CASCADE, related_name='events_author', null=True)
subject_companies = models.ManyToManyField('core.organisation', related_name='events_companies')
legacy_id = models.CharField(max_length=128, blank=True)
event_date = models.DateField(null=True, blank=True)
event_time = models.TimeField(null=True, blank=True)
disclosed_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return '{}: {}'.format(self.author, self.event_type)
class Meta:
ordering = ('pk',)
class EventType(models.Model):
language = models.ForeignKey(
'core.Language',
default=get_default_language,
null=True,
on_delete=models.SET_NULL,
related_name='event_contents'
)
name = models.CharField(max_length=64, default=None)
key = models.CharField(max_length=64, default=None)
def __str__(self):
return '{}'.format(self.name)
class Meta:
ordering = ('pk',)
景色
class NewsEventList(generics.ListCreateAPIView):
permission_classes = (IsAuthenticatedAndOfSameOrganisationEvents,)
serializer_class = NewsEventSerializer
def get_queryset(self):
org_pk = self.kwargs.get('pk', None)
try:
org_obj = Organisation.objects.get(pk=org_pk)
except Organisation.DoesNotExist:
return ValidationError('Organisation does not exist')
news_events = NewsEvent.objects.filter(author=org_obj)
return news_events
序列化器
class OrganisationNameSerializer(ModelSerializer):
class Meta:
model = Organisation
fields = ['pk']
class EventTypeSerializer(ModelSerializer):
class Meta:
model = EventType
fields = ['pk']
class HeadlineSerializer(ModelSerializer):
class Meta:
model = EventHeadline
fields = ['news_event', 'language', 'headline']
class NewsEventSerializer(ModelSerializer):
event_type = EventTypeSerializer()
author = OrganisationNameSerializer()
subject_companies = OrganisationNameSerializer(many=True)
class Meta:
model = NewsEvent
fields = ['pk', 'event_type', 'author', 'event_date', 'event_time', 'disclosed_at', 'subject_companies', 'created_at', 'updated_at']
def create(self, validated_data):
# Get PK for organisation from URL
org_pk = self.context.get('request').parser_context.get('kwargs', {}).get('pk', {})
org_obj = Organisation.objects.get(pk=org_pk)
print(self.context.get('request').data)
pprint(validated_data)
另外,为了参考,我为一个已经存在的 NewsEvent 实例打印了 serializer.data:
news_event_test = NewsEvent.objects.all()[0]
serializer = NewsEventSerializer(news_event_test)
print(serializer.data)
{'pk': 1, 'event_type': OrderedDict([('pk', 1)]), 'author': OrderedDict([('pk', 1)]), 'event_date': None, 'event_time': None, 'disclosed_at': None, 'subject_companies': [OrderedDict([('pk', 1)])], 'created_at': '2021-10-25T09:32:41.562428+02:00', 'updated_at': '2021-10-25T09:32:41.562487+02:00'}
我也尝试过对 validated_object 中的每个字段进行“弹出”,但只有那些不是空的 OrderedDict 工作,例如 disclosed_at,但如果我尝试这样做:
event_type = validated_data.pop('event_type')
我得到:
KeyError: "Got KeyError when attempting to get a value for field `event_type` on serializer `NewsEventSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'event_type'."
event_type = models.ForeignKey('publication.EventType', on_delete=models.SET_NULL, related_name='events_type', null=True)
author = models.ForeignKey('core.Organisation', on_delete=models.CASCADE, related_name='events_author', null=True)
subject_companies = models.ManyToManyField('core.Organisation', related_name='events_companies')
替换那些行并删除以前的迁移并创建新的并尝试。
原因是我正在执行写入操作(POST 请求,以 Django/DRF 术语创建操作)并且序列化程序上的 pk 字段默认为 read_only。
要在写入操作期间验证该数据,您需要明确设置该字段并设置 read_only=False。例如:
class RedditTestSerializer(serializers.ModelSerializer):
class Meta:
model = models.Task
fields = ("pk",)
这解决了问题,但您可能需要考虑使用 PrimaryKeyRelatedField 如果您的用例仅设置主键 相关对象的。