#django #django-rest-framework
Вопрос:
Может ли кто-нибудь помочь мне понять, почему некоторые поля неправильно анализируются с помощью вложенных сериализаторов с Django и Django-rest-framework?
Я исследовал проблему на SO, и единственная причина, по которой это происходит, которую я обнаружил, заключается в том, что запрос отправляется в виде данных формы, а не в формате Json, но я проверил этот ответ.content_type равен application/json-так что здесь это не должно быть проблемой.
Вот как выглядят мои проверенные данные (обратите внимание, что 3 поля содержат только пустой OrderedDict):
{'author': OrderedDict(), 'disclosed_at': datetime.datetime(2021, 10, 19, 12, 0, tzinfo=lt;DstTzInfo 'Europe/Stockholm' CEST 2:00:00 DSTgt;), 'event_date': datetime.date(2021, 10, 20), 'event_type': OrderedDict(), 'subject_companies': [OrderedDict()]}
Вот о чем просьба.данные выглядят так (где вы можете видеть, что все поля есть, и имеют поля, указанные каждым сериализатором, указанные ниже в этом абзаце).:
{'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',)
The view
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
Serializers
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)
Также для справки я распечатал сериализатор.данные для уже существующего экземпляра новостного события:
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, но только те, которые этого не делают, являются пустой упорядоченной работой, такой как 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'."
Комментарии:
1. можете ли вы добавить фрагмент кода вашего представления?
2. Я добавил представление @Shaonsani
3. Также добавьте модели !
4. Добавлены модели @Shaonsani
5. просто попробуйте мой ответ и дайте мне знать
Ответ №1:
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')
Замените эти строки и удалите предыдущие миграции, создайте новые и попробуйте.
Комментарии:
1. Не видите, что вы внесли какие-то коррективы?
Ответ №2:
Причина заключалась в том, что я выполнял операцию записи (запрос POST, действие создания в терминах Django/DRF), а поле pk в сериализаторах по умолчанию только для чтения.
Чтобы проверить эти данные во время операций записи, вам нужно будет явно задать поле и установить read_only=False. Например:
class RedditTestSerializer(serializers.ModelSerializer): class Meta: model = models.Task fields = ("pk",)
Это решило проблему, но вы, возможно, захотите рассмотреть возможность использования Primarykeyrelatedполе, если в вашем случае использования задается только первичный ключ связанного объекта.