Динамический сериализатор Django REST: «Ошибка атрибута: объект «Сериализатор» не имеет атрибута «pk»»

#python #python-3.x #django #django-rest-framework

Вопрос:

Я хочу иметь возможность динамически изменять значение сериализатора в зависимости от контекста или на основе атрибутов объекта.

Для этой цели я сделал сериализатор, который может быть изменен в __ init __ на основе приведенной контекстной информации. Но я пытаюсь вызвать его для создания нового пользователя, что обычно работает, но с пользователем вложенного поля что-то не так…

Вот мой код:

 class UsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = '__all__'
        extra_kwargs = {'password': {'write_only': True}}


    user_permissions = serializers.SerializerMethodField('get_user_permissions')
    groups = serializers.SerializerMethodField('get_groups')


    def get_user_permissions(self, user):
        LOGGER.info(list(user.user_permissions.all().values_list('id', flat=True)))
        return list(user.user_permissions.all().values_list('id', flat=True))


    def get_groups(self, user):
        return list(user.groups.all().values_list('id', flat=True))


class UsersSerializerGeneralInfo(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email', 'username']


class AccessSerializer(serializers.ModelSerializer):
    class Meta:
        model = Access
        fields = '__all__'


class SNHUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = SNHUser
        fields = '__all__'


    def __init__(self, *args, **kwargs):
        super().__init__(self, *args, **kwargs)
        # If raw, not getting any additional fields
        if not self.context.get("get_raw", False):
            # Retrieving access if given in context
            if self.context.get("get_accesses", False):
                self.fields['accesses'] = serializers.SerializerMethodField()
            # Retrieving all user information if given in context
            if not self.context.get("get_all_user_informations", False):
                self.fields['user'] = UsersSerializer()
            else:
                self.fields['user'] = UsersSerializerGeneralInfo()
            # Additional fields if not raw
            self.fields['implants'] = serializers.SerializerMethodField()
            self.fields['users_clinical_trials'] = serializers.SerializerMethodField()
            self.fields['implant_patients'] = serializers.SerializerMethodField()


    ## Save function
    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)


    ## Validation method method used to validate field
    def clean(self):
        validate_password(self.user.password)


    def create(self, validated_data):
        # Create first the django user
        user_data = validated_data.pop('user')
        user = User.objects.create_user(**user_data)
        # Create the layer of profile for the user
        return SNHUser.objects.create(user=user, **validated_data)


    def to_representation(self, obj):
        return super(SNHUserSerializer, self).to_representation(obj)


    def get_accesses(self, obj):
        # If context parameter is given, adding accesses
        if self.context.get("get_accesses", False):
            accesses = Access.objects.filter(snh_user=obj.id)
            ser = AccessSerializer(accesses, many=True)
            return ser.data
        else:
            return None


    def get_implants(self, obj):
        # If patient, returning implants linked else None
        if obj.role == "Patient":
            from apps.provisioning.serializers import ImplantSerializer
            from apps.medicalmanagement.module.implant_patient import get_implants_by_patient
            implants_o = get_implants_by_patient(obj)
            if implants_o != []:
                ser = ImplantSerializer(implants_o, many=True)
                return ser.data
            else:
                return implants_o
        return None


    def get_users_clinical_trials(self, obj):
        from apps.medicalmanagement.models import UserClinicalTrial
        from apps.medicalmanagement.serializers import UserClinicalTrialSerializer
        from apps.medicalmanagement.module.user_clinical_trial import get_users_clinical_trials
        users_clinical_trials_o: UserClinicalTrial = get_users_clinical_trials(
            snh_user=obj
        )
        return UserClinicalTrialSerializer(users_clinical_trials_o, many=True).data


    def get_implant_patients(self, obj):
        # If patient, returning implants patients links else None
        if obj.role == "Patient":
            from apps.medicalmanagement.models import ImplantPatient
            from apps.medicalmanagement.serializers import ImplantPatientSerializer
            from apps.medicalmanagement.module.implant_patient import get_implant_patients
            implant_patient_o: ImplantPatient = get_implant_patients(patient=obj)
            return ImplantPatientSerializer(implant_patient_o, many=True).data
        else:
            return None
 

Проблема в следующем: когда я пытаюсь создать экземпляр для записи и создаю нового пользователя, я получаю эту ошибку:

     if ser.is_valid():

  File "libsite-packagesrest_frameworkserializers.py", line 220, in is_valid

    self._validated_data = self.run_validation(self.initial_data)

  File "site-packagesrest_frameworkserializers.py", line 421, in run_validation

    self.run_validators(value)

  File "site-packagesrest_frameworkserializers.py", line 454, in run_validators

    super().run_validators(to_validate)

  File "site-packagesrest_frameworkfields.py", line 591, in run_validators

    validator(value, self)

  File "libsite-packagesrest_frameworkvalidators.py", line 151, in __call__

    queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)

  File "libsite-packagesrest_frameworkvalidators.py", line 144, in exclude_current_instance

    return queryset.exclude(pk=instance.pk)

AttributeError: 'SNHUserSerializer' object has no attribute 'pk'
 

Я не определял никаких атрибутов pk, поэтому я полагаю, что они добавляются автоматически.

Вот как я вызвал свой сериализатор, просто передавая данные без каких-либо аргументов.

 ser = SNHUserSerializer(data=data)
if ser.is_valid():
 

То, что я делал раньше, — это (и это хорошо работало, но не может быть изменено при инициализации):

 class SNHUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = SNHUser
        fields = '__all__'


    user = UsersSerializer()


    def create(self, validated_data):
        # Create first the django user
        user_data = validated_data.pop('user')
        user = User.objects.create_user(**user_data)
        # Create the layer of profile for the user
        return SNHUser.objects.create(user=user, **validated_data)


    def to_representation(self, obj):
        return super(SNHUserSerializer, self).to_representation(obj)
 

Есть ли у вас какие-либо идеи о том, что может вызвать эту проблему ?

Спасибо

Комментарии:

1. Можете ли вы поделиться своими взглядами

2. @bdbd Добавил информацию, ничего особенного, просто обычный звонок. Это вложенное отношение пользователя, которое вызывает проблемы, но не может понять, в чем именно заключается проблема…

Ответ №1:

Эта проблема вызвана этой строкой:

 super().__init__(self, *args, **kwargs)
 

Вам нужно удалить self :

 super().__init__(*args, **kwargs)