Reputation: 144
I want to be able to dynamically change my serializer value based on context or based on attributes of the object.
For that purpose I did a serializer that can be modified in __ init __ based on the context information given. But I'm trying to call it to creates a new user, which usually works, but there is something wrong with the nested field user...
Here my code:
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
Problem is: when I try to instanciate to write and creates a new user I get this error:
if ser.is_valid():
File "\lib\site-packages\rest_framework\serializers.py", line 220, in is_valid
self._validated_data = self.run_validation(self.initial_data)
File "\site-packages\rest_framework\serializers.py", line 421, in run_validation
self.run_validators(value)
File "\site-packages\rest_framework\serializers.py", line 454, in run_validators
super().run_validators(to_validate)
File "\site-packages\rest_framework\fields.py", line 591, in run_validators
validator(value, self)
File "\lib\site-packages\rest_framework\validators.py", line 151, in __call__
queryset = self.exclude_current_instance(attrs, queryset, serializer.instance)
File "\lib\site-packages\rest_framework\validators.py", line 144, in exclude_current_instance
return queryset.exclude(pk=instance.pk)
AttributeError: 'SNHUserSerializer' object has no attribute 'pk'
I didn't define any pk attributes so I suppose they are added automatically.
Here is how I called my serializer, just passing up data in without any arguments.
ser = SNHUserSerializer(data=data)
if ser.is_valid():
What I was doing before is this (and it worked well but cannot be changed on initialization):
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)
Have you any idea of what may cause this problem ?
Thanks
Upvotes: 1
Views: 943
Reputation: 12068
This issue is caused by this line:
super().__init__(self, *args, **kwargs)
You need to remove self
:
super().__init__(*args, **kwargs)
Upvotes: 1