Reputation: 1241
I am trying to insert form-data into models User
and UserGroup
using APIView
.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=255, blank=False, null=False)
last_name = models.CharField(max_length=255, blank=False, null=False)
profile_picture = models.ImageField(upload_to='profile_pictures/', max_length=None, null=True, blank=True)
is_active = models.BooleanField(default=True)
objects = UserManager()
USERNAME_FIELD = 'email'
class UserGroup(models.Model):
user = models.ForeignKey(User, related_name='user_id', on_delete=models.CASCADE, blank=False)
role = models.ForeignKey(Role, related_name='role_id', on_delete=models.CASCADE, blank= False)
I am able to insert data into model User
but when I try to do the same with the model UserGroup
it returns an error.
ValueError: Cannot assign "17": "UserGroup.user" must be a "User" instance.
I want this API to create a user and assign a role immediately to the newly created user by inserting role_id
and user_id
in model UserGroup
.
I have created two different serializer UserSerializer
and UserGroup
serializer and both has its own create method to push an instance into model. Also, in post request payload I am accepting all the fields of User
model but for UserGroup
model I am just accepting role
field and not the user
field as its value needs to be generated once the user is created.
I will have to use transaction as well to roll back if in case a user is created but the API has failed to assign the role to that user. So, I want to know is this an appropriate way to accomplish this task. class UserAPIViews(APIView):
permission_classes = [IsAuthenticated]
parser_classes = (FormParser, MultiPartParser)
def post(self, request, format=None):
user_group={} #dictionary for UserGroupSerializer
user_group['role'] = request.data['user_group'] # adding Role object to UserGroup dictionary from request payload.
del request.data['user_group'] # removing user_group (key,value) from the payload so that dictionary could used for UserSerializer
user_serialized_data = serializers.UserSerializer(data=request.data) # Serializing request payload dictionary with the UserSerializer
if user_serialized_data.is_valid(): # check the dictionary is validated or invalidated by the UserSerializer
try:
user_created_with_id = user_serialized_data.save() # if the dictionary is validated save the UserSerilaized instance to the User model.
# if the reqest payload dictionary is saved successfully then the save() will return the id of newly created object.
except IntegrityError as error:
message = f'Email already exist.'
return Response ({
'error_message' : message,
'status' : status.HTTP_226_IM_USED
})
else:
return Response ({'user_serilization_error':user_serialized_data.error_messages}) # return Payload dictionary has been invalidated by the UserSerilaizer.
# add the user parameter to the user_group dictionary. The value of key user should be the object id of newly created user.
user_group['user'] = user_created_with_id
# now the UserGroupSerilaizer dictionary is ready.
# user_group ={"user":user_created_with_id, "role":<Role: Role object (2)>}
# serilaize the user_group dictionary with the UserGroupSerilaizer.
# this is where it generates a ValueError stating that UserGroup.user must be a User instance.
user_group_serialized_data = serializers.UserGroupSerializer(data=user_group)
if user_group_serialized_data.is_valid():
user_group_id = user_group_serialized_data.save()
success_message = 'User has been created.'
return Response({
'success_message' : success_message,
'status' : status.HTTP_201_CREATED
})
else:
return Response ({'user_group_serilization_error':user_serialized_data.error_messages})
UserGroupSerializer
class UserGroupSerializer(serializers.Serializer):
class Meta:
model = UserGroup
user = serializers.IntegerField(required=True)
role = serializers.IntegerField(required=True)
def create(self, validated_data):
user_role_relation_id = UserGroup.objects.create(**validated_data)
return user_role_relation_id
UserSerializer
class UserSerializer(serializers.Serializer):
class Meta:
model = User
id = serializers.IntegerField(read_only=True)
email = serializers.EmailField(required=True)
first_name = serializers.CharField(max_length=255, required=True)
last_name = serializers.CharField(max_length=255, required=True)
profile_picture = serializers.ImageField(max_length=None, allow_empty_file=True, use_url= False, required=False)
password = serializers.CharField(max_length = 255, write_only=True, required=True)
def create(self, validated_data):
user_id = User.objects.create_user(**validated_data)
return user_id
API request has the payload as stated below -
{
"first_name":"", # Serialized by UserSerializer
"last_name":"", # Serialized by UserSerializer
"email":"", # Serialized by UserSerializer
"password":"", # Serialized by UserSerializer
"profile_picture":"", # Serialized by UserSerializer
"user_role:":"" # Serialized by UserGroupSerializer
}
I had to split this payload into two different dictionaries, one to be serialized by UserSerializer
and another to be serialized by UserGroupSerializer
.
Please read the comments of APIView class to understand the flow.
Upvotes: 0
Views: 374
Reputation: 919
The issue seems to be in the UserGroupSerializer
have you looked at this: https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
(you shouldn't have to adjust the
user_group['user']
sorry for the confusion in the comment)
Upvotes: 0
Reputation: 357
ValueError: Cannot assign "17": "UserGroup.user" must be a "User" instance. this error says that have to give a user instanceas input UserGroup.user = user_object
you can do that by :
current_user = User.objects.get(id = 17)
UserGroup.user = current_user
or
UserGroup.user = request.user
Upvotes: 1