Как объединить UserProfile и модель User, которые связаны через отношение OneToOne, в одну конечную точку?
У меня есть пользовательский класс пользователя и класс профиля. Класс профиля имеет отношение OneToOne с пользовательским User. Сериализатор имеет User как Meta модель с добавлением модели профиля в новое поле profile, расширенное на кортеж полей. но когда я пытаюсь получить детальное представление, он возвращает ошибку, говоря, что поле Profile не является атрибутом CustomUser. Я буду благодарен, если вы просмотрите код, который я добавил ниже, и поможете мне в этом.
Модель пользователя:
class CustomUser(AbstractBaseUser, PermissionsMixin):
class Types(models.TextChoices):
DOCTOR = "DOCTOR", "Doctor"
PATIENT = "PATIENT", "Patient"
# what type of user
type = models.CharField(_("Type"), max_length=50, choices=Types.choices, null=True, blank=False)
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomBaseUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'type'] #email is required by default
def get_full_name(self):
return self.name
def __str__(self):
return self.email
Модель профиля:
class DoctorProfile(models.Model):
"""Model for Doctors profile"""
class DoctorType(models.TextChoices):
"""Doctor will choose profession category from enum"""
PSYCHIATRIST = "PSYCHIATRIST", "Psychiatrist"
PSYCHOLOGIST = "PSYCHOLOGIST", "Psychologist"
DERMATOLOGIST = "DERMATOLOGIST", "Dermatologist"
SEXUAL_HEALTH = "SEXUAL HEALTH", "Sexual health"
GYNECOLOGIST = "GYNECOLOGIST", "Gynecologist"
INTERNAL_MEDICINE = "INTERNAL MEDICINE", "Internal medicine"
DEVELOPMENTAL_THERAPIST = "DEVELOPMENTAL THERAPIST", "Developmental therapist"
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
doctor_type = models.CharField(
_("Profession Type"),
max_length=70,
choices=DoctorType.choices,
null=True,
blank=False)
title = models.IntegerField(_('Title'), default=1, choices=TITLES)
date_of_birth = models.DateField(null=True, blank=False)
gender = models.IntegerField(_('Gender'), default=1, choices=GENDERS)
registration_number = models.IntegerField(_('Registration Number'), null=True, blank=False)
city = models.CharField(_('City'), max_length=255, null=True, blank=True)
country = models.CharField(_('Country'), max_length=255, null=True, blank=True)
def __str__(self):
return f'profile-{self.id}-{self.title} {self.owner.get_full_name()}'
Serializer:
class DoctorProfileFields(serializers.ModelSerializer):
"""To get the fields from the DoctorProfile. it will be used in the 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()
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
Вид:
class DoctorProfileAPIView(generics.RetrieveUpdateDestroyAPIView):
"""To get the doctor profile fields and update and delete"""
serializer_class = DoctorProfileSerializer
queryset = User.objects.all()
def get_object(self):
return get_object_or_404(User, id=self.request.user.id, is_active=True)
Я хочу получить json-ответ в детальном представлении, как показано ниже:
{
"name": the name,
"avatar": avatar,
"profile": {
"doctor_type": "PSYCHIATRIST",
"title": 1,
"date_of_birth": 11-11-1990,
"registration_number": 21547,
}
}
Может ли кто-нибудь помочь мне в этом? Или есть какой-либо другой подход к проектированию, который соответствует моей цели. Моя цель состоит в том, чтобы информация о пользователе + информация о профиле были объединены в одной конечной точке как целый профиль во фронтенде, из которого пользователь будет видеть/редактировать профиль.
В первую очередь переместите внешний ключ OneToOne в модели CustomUser, добавьте:
owner = models.OneToOneField('DoctorProfile', on_delete=models.CASCADE, related_name='doctor_profile')
и удалить из DoctorProfile:
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='doctor_profile')
Сделайте все миграции, и теперь вам нужно установить новые данные в db. В сериализаторах вы правильно используете Nested relationships, поэтому добавьте атрибут many, установленный в False:
profile = DoctorProfileFields(many=False)