Handling user registration and subsequent profile creation
I have a custom User model with a role field and respective [Role]Profile models:
class User(AbstractUser):
role = models.CharField()
# Other fields
class TeacherProfile(models.Model):
# profile_picture and other fields
class StudentProfile(models.Model):
# for now just a reference to the user model
user = models.OneToOneField(User, ...)
These models have their respective serializers. I am using the djoser library for handling user authentication. Now the default way I use to handle user creation and profile management would be:
- Client sends
userpayload only - stuff like name, email, role etc. - Use
post_savesignal to create the appropriate profile object by determining therolefield. - Then to upload profile related fields, simply send a
PATCHrequest for that profile.
The issue is the client wants to send the whole thing at once. They have a registration form which contains auth fields and also profile fields like profile_pic or about field or whatever. This is what ChatGPT suggested:
class CustomUserCreateSerializer(UserCreateSerializer):
teacher_profile = TeacherProfileSerializer(required=False)
student_profile = StudentProfileSerializer(required=False)
class Meta(UserCreateSerializer.Meta):
model = User
fields = (
"id",
"email",
"password",
"role",
"first_name",
"last_name",
"teacher_profile",
"student_profile",
)
def create(self, validated_data):
teacher_profile_data = validated_data.pop("teacher_profile", None)
student_profile_data = validated_data.pop("student_profile", None)
user = super().create(validated_data)
# Signals will create the profile automatically, we just update it if data was provided
if user.role == User.TEACHER and teacher_profile_data:
profile, created = TeacherProfile.objects.get_or_create(user=user)
for attr, value in teacher_profile_data.items():
setattr(profile, attr, value)
profile.save()
elif user.role == User.STUDENT and student_profile_data:
profile, created = StudentProfile.objects.get_or_create(user=user)
for attr, value in student_profile_data.items():
setattr(profile, attr, value)
profile.save()
return user
class CustomUserCreatePasswordRetypeSerializer(UserCreatePasswordRetypeSerializer):
teacher_profile = TeacherProfileSerializer(required=False)
student_profile = StudentProfileSerializer(required=False)
class Meta(UserCreatePasswordRetypeSerializer.Meta):
model = User
fields = (
"id",
"email",
"password",
"role",
"first_name",
"last_name",
"teacher_profile",
"student_profile",
)
def create(self, validated_data):
teacher_profile_data = validated_data.pop("teacher_profile", None)
student_profile_data = validated_data.pop("student_profile", None)
user = super().create(validated_data)
if user.role == User.TEACHER and teacher_profile_data:
profile, created = TeacherProfile.objects.get_or_create(user=user)
for attr, value in teacher_profile_data.items():
setattr(profile, attr, value)
profile.save()
elif user.role == User.STUDENT and student_profile_data:
profile, created = StudentProfile.objects.get_or_create(user=user)
for attr, value in student_profile_data.items():
setattr(profile, attr, value)
profile.save()
return user
class CustomUserSerializer(UserSerializer):
class Meta(UserSerializer.Meta):
model = User
fields = ("id", "email", "role", "first_name", "last_name")
I don't know if this is the right way to go, for some reason it feels wrong. Can anyone suggest how I should handle this. Now the payload looks like this:
{
"email": "user@example.com",
"password": "string",
"role": "admin",
"first_name": "string",
"last_name": "string",
"teacher_profile": {
"profile_picture": "string",
"professional_title": "string",
"location": "string",
"about": "string",
"education": "string",
"achievements": "string",
"consultation_rate": "51251202",
"user": 0
},
"student_profile": {
"user": 0
},
"re_password": "string"
}