Сериализатор DRF принимает вложенную полезную нагрузку JSON как один объект JSON

Пожалуйста, просмотрите описание, я попытался описать все, с чем я столкнулся, пытаясь решить эту проблему.

У меня есть две модели, User и Profile. Я пытаюсь обновить их через один сериализатор. Я объединил две модели в один сериализатор, как показано ниже:

class DoctorProfileFields(serializers.ModelSerializer):
    """this will be used as value of profile key in DoctorProfileSerializer"""
    class Meta:
        model = DoctorProfile
        fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number', 'gender', 'city', 'country', )

class DoctorProfileSerializer(serializers.ModelSerializer):
    """retrieve, update and delete profile"""

    profile = DoctorProfileFields(source='*')
    class Meta:
        model = User
        fields = ('name', 'avatar', 'profile', )
        
    @transaction.atomic
    def update(self, instance, validated_data):
        ModelClass = self.Meta.model
        profile = validated_data.pop('profile', {})
        ModelClass.objects.filter(id=instance.id).update(**validated_data)

        if profile:
            DoctorProfile.objects.filter(owner=instance).update(**profile)
        new_instance = ModelClass.objects.get(id = instance.id)
        return new_instance 

Когда я отправляю запрос методом GET, DoctorProfileSerializer возвращает вложенные данные (комбинируя две модели User и DoctorProfile) в нужном виде. Но когда я пытаюсь обновить обе модели через этот сериализатор, он возвращает ошибку User has no field named 'doctor_type'.

Давайте посмотрим на JSON, который я пытаюсь отправить:

{
    "name": "Dr. Strange updated twice",
    "profile" : {
        "doctor_type": "PSYCHIATRIST"
    }
    
}

Давайте посмотрим, как сериализатор получает JSON:

{
    "name": "Maruf updated trice",
    "doctor_type": "PSYCHIATRIST"
    
}

Как узнать, что сериализатор получает неправильный JSON? Я отладил validated_data в DoctorProfileSerializer, и он показывает, что это плоский JSON, нет ключа с именем profile.

Я предполагаю, что проблема связана с источником, который я добавил в DoctorProfileSerializer. Но если я не использую источник, метод get возвращает следующую ошибку Got AttributeError when attempting to get a value for field profile on serializer (DoctorProfileSerializer).

Пожалуйста, сообщите мне, если это решаемо, и если это хороший подход, чтобы сделать это таким образом?

Ок, извините, если мой ответ слишком длинный, но позвольте мне попытаться ответить шаг за шагом,

Модели:

class DoctorProfile(models.Model):
    # everything as it is
    # except I feel comfortable using ForeignKey :D
    owner = models.ForeignKey(
        CustomUser, 
        on_delete=models.CASCADE, 
        related_name='doctor_profile'
    )
    # everything as it is

class CustomUser(AbstractBaseUser, PermissionsMixin):
# as it is

Сериализаторы:

class DoctorProfileSerializer(serializers.ModelSerializer):
"""Serializer for DoctorProfile."""

    class Meta(object):
        model = DoctorProfile
        fields = [
            'id',
            'doctor_type', 
            'title', 
            'date_of_birth', 
            'registration_number', 
            'gender', 
            'city', 
            'country', 
        ]
        read_only_fields = [
            'id',
        ]

class CustomUserSerializer(serializers.ModelSerializer):
"""Serializer for DoctorProfile."""
    
    # here I'm renaming the related object exactly as the 
    # related name you've provided on model
    doctor_profile = DoctorProfileSerializer(many=False)

    class Meta(object):
        model = CustomUser
        fields = [
            'name', 
            'avatar', 
            'doctor_profile', 
        ]
        read_only_fields = [
            'id',
        ]

    def update(self, instance, validated_data):
        # instance is the current row of CustomUser
        # validated_data is the new incoming data
        # use validated_data.pop('doctor_profile') to extract
        # doctor_profile data and do whatever is needed on 
        # DoctorProfile model 
        # compare them and perform your update method
        # as you wish on the DoctorProfile model
    # object after updating models, you can query the total
    # object again before returning if you want
    return updated_object   

Вид:

class CustomUserAPIView(RetrieveUpdateAPIView):
    """CustomUserAPIView."""

    permission_classes = [IsAuthenticated]
    model = CustomUser
    serializer_class = CustomUserSerializer
    queryset = CustomUser.objects.all()
    lookup_field = 'id'

    def update(self, request, *args, **kwargs):
        """Update override."""
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(
            instance,
            data=request.data,
            partial=partial,
        )
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        custom_user_obj = CustomUser.objects.filter(
            id=instance.id,
        ).first()
        serializer = CustomUserSerializer(custom_user_obj)
        return Response(serializer.data)

Запустите миграцию и сообщите мне, получаете ли вы ожидаемый результат по методу GET. Для метода UPDATE если вы столкнетесь с какой-либо проблемой, дайте мне знать, я сразу же обновлю ответ соответствующим образом.

Для того, чтобы держать под рукой всю документацию по Django Rest Framework, используйте эту ссылку https://www.cdrf.co/

Вернуться на верх