We are having too many models which are related, While returning queryset serializing the data is too slow(serializer.data). Below are our models and serializer.
Why django nested serializer is taking too long to return JSON response. What are we doing wrong here?
models.py
class Doctor(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
first_name = models.CharField(_("first_name"), max_length=255, null=False)
last_name = models.CharField(_("last_name"), max_length=255, null=False)
full_name = property(lambda self: "{} {}".format(self.first_name, self.last_name))
email = models.EmailField(_("email"), max_length=255, unique=True, null=False)
password = models.CharField(_("password"), max_length=128, null=False)
country_code = models.CharField(
_("country_code"), max_length=10, null=False, blank=False
)
phone_number = models.CharField(
_("phone_number"), max_length=50, null=False, blank=False
)
full_phone_number = models.CharField(
_("full_phone_number"), max_length=60, unique=True, null=False
)
is_otp_verified = models.BooleanField(_("is_otp_verified"), default=False)
gender = models.CharField(
_("gender"), max_length=50, choices=choices.gender_choices, blank=True, null=False
)
image = encrypt(models.ImageField(upload_to=userProfilePictureDirectoryPath, default=None, null=True, blank=True))
is_active = models.BooleanField(_("is_active"), default=True)
is_verified = models.BooleanField(_("is_verified"), default=False)
national_medical_id = models.CharField(max_length=100, null=True, unique=True)
numeric_id = models.IntegerField(blank=True, null=True)
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True, blank=True, null=True)
username = None
USERNAME_FIELD = "full_phone_number"
REQUIRED_FIELDS = []
objects = CustomUserManager()
class Meta:
unique_together = (
"country_code",
"phone_number",
)
def __str__(self) -> str:
return self.phone_number
class Specialties(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name_en = models.CharField(_("name_en"), max_length=255, blank=True, null=True)
name_ro = models.CharField(_("name_ro"), max_length=255, blank=True, null=True)
name_ar = models.CharField(_("name_ar"), max_length=255, blank=True, null=True)
priority = models.IntegerField(_("priority"), default=settings.DEFAULT_SPECIALITY_PRIORTY)
icon = encrypt(models.ImageField(upload_to=userSpecialtiesDirectoryPath, blank=True))
is_active = models.BooleanField(_("is_active"), default=False)
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True, blank=True, null=True)
class Meta:
ordering = ['priority']
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
doctor = models.OneToOneField(to=Doctor, on_delete=models.CASCADE)
bio = models.TextField(max_length=5000, blank=True)
spoken_languages = models.ManyToManyField(to=Language, name=_("language"))
graduation_year = models.CharField(max_length=4, blank=True)
app_language = models.CharField(max_length=255, choices=choices.language_choices, default=choices.language_choices[0][0])
date_of_birth = models.DateField(blank=True, null=True)
reviews_average = models.FloatField(default=0.0)
number_of_bookings = models.IntegerField(default=0)
number_of_reviews = models.IntegerField(default=0)
number_of_visitors = models.IntegerField(default=0)
image = encrypt(models.ImageField(upload_to=userProfilePictureDirectoryPath, default=None, null=True, blank=True))
lowest_price = models.FloatField(null=True, blank=True)
specialties = models.ManyToManyField(to=Specialties, name="specialties")
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True, blank=True, null=True)
class Achievements(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
description = models.TextField(
_("description"), max_length=1024, blank=True, null=True
)
name = models.CharField(blank=True, null=True, max_length=255)
image = encrypt(models.ImageField(upload_to=userAchivementsDirectoryPath, blank=True))
is_active = models.BooleanField(_("is_active"), default=False)
issued_at = models.DateTimeField(_("issued_at"), blank=True, null=True)
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True, blank=True, null=True)
class PlaceOfWork(models.Model):
id = models.UUIDField(
primary_key=True, default=uuid.uuid4, unique=True, editable=False
)
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
title = models.CharField(max_length=255, blank=False, null=False)
name = models.CharField(max_length=255, blank=False, null=False)
still_working = models.BooleanField(default=True)
started_at = models.DateField(blank=False, null=False)
finished_at = models.DateField(blank=True, null=True)
created_at = models.DateTimeField(_("created_at"), auto_now_add=True)
updated_at = models.DateTimeField(_("updated_at"), auto_now=True, blank=True, null=True)
views.py
class DoctorBySpecialityView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
doctor = request.user
specialties = Specialties.objects
active_doctor = get_all_active_doctors().count()
search = request.query_params.get(constants.SEARCH, None)
context = {"me": doctor, "search": search}
serialized_specialties = SpecialitiesWithListOfDoctors(
specialties, many=True, context=context
)
data = {
"specialties": serialized_specialties.data,
"active_doctor": active_doctor,
}
response = get_response(
status=status.HTTP_200_OK,
status_code=ResponseStatus.success.value,
data=data,
)
return Response(response, status=status.HTTP_200_OK)
serializers.py
class SpecialtiesBaseSerializer(serializers.ModelSerializer):
class Meta:
model = Specialties
read_only_fields = ("id",)
class SpecialtiesSerializer(SpecialtiesBaseSerializer):
class Meta(SpecialtiesBaseSerializer.Meta):
fields = "__all__"
class SpecialitiesWithListOfDoctors(SpecialtiesSerializer):
doctors = serializers.SerializerMethodField()
doctor_count = serializers.SerializerMethodField()
def get_doctors(self, speciality):
from doctor.serializers import DoctorProfileSerializer
me = self.context["me"]
search = self.context["search"]
query = speciality.profile_set.filter(~Q(doctor=me)).all()
if not search:
query = query.filter(
Q(doctor__first_name__icontains=search)
| Q(doctor__last_name__icontains=search)
)
serialized_doctor_profile = DoctorProfileSerializer(
query, many=True, context=self.context
)
self.context["doctor_count"] = query.count()
return serialized_doctor_profile.data
def get_doctor_count(self, speciality):
return self.context["doctor_count"]
class DoctorProfileSerializer(ProfileSerializer):
doctor = serializers.SerializerMethodField()
place_of_work = serializers.SerializerMethodField()
achievements = serializers.SerializerMethodField()
chat_access = serializers.SerializerMethodField()
def get_doctor(self, profile):
doctor = profile.doctor
return DoctorSerializer(doctor).data
def get_place_of_work(self, profile):
place_of_work = profile.doctor.placeofwork_set.all()
return PlaceOfWorkSerializer(place_of_work, many=True).data
def get_achievements(self, profile):
achievements = profile.doctor.achievements_set.all()
return AchievementsSerializer(achievements, many=True).data
class DoctorBaseSerializer(serializers.ModelSerializer):
class Meta:
model = Doctor
class DoctorSerializer(DoctorBaseSerializer):
need_support_help = serializers.SerializerMethodField()
def create(self, validated_data):
validated_data["password"] = make_password(validated_data["password"])
validated_data["numeric_id"] = get_numeric_id()
return super(DoctorSerializer, self).create(validated_data)
class Meta(DoctorBaseSerializer.Meta):
read_only_fields = ("uuid",)
exclude = [
"numeric_id",
]
extra_kwargs = {
"password": {"write_only": True},
"is_superuser": {"write_only": True},
"is_staff": {"write_only": True},
"date_joined": {"write_only": True},
"groups": {"write_only": True},
"user_permissions": {"write_only": True},
"last_login": {"write_only": True},
}
def get_need_support_help(self, doctor):
#using some other models
support_help, created = SupportHelp.objects.get_or_create(doctor=doctor)
need_help = doctor.supporthelp.need_help
return need_help
class PlaceOfWorkBaseSerializer(serializers.ModelSerializer):
class Meta:
model = PlaceOfWork
read_only_fields = ("id",)
def validate(self, data):
if self.partial:
return super().validate(data)
if data.get("finished_at"):
data["still_working"] = False
if data["finished_at"] < data["started_at"]:
raise serializers.ValidationError(
{"finished_at": "finished_at should be greater than started_at"}
)
still_working = data.get("still_working", True)
if not still_working and not data.get("finished_at"):
raise serializers.ValidationError(
{"finished_at": "finished_at is required if still_working is false"}
)
return super().validate(data)
class PlaceOfWorkSerializer(PlaceOfWorkBaseSerializer):
class Meta(PlaceOfWorkBaseSerializer.Meta):
fields = "__all__"
class AchievementsBaseSerializer(serializers.ModelSerializer):
class Meta:
model = Achievements
read_only_fields = ("id",)
def validate_image(self, image):
if not validate_image_size(image):
raise serializers.ValidationError(
f"image should be between {settings.MIN_IMAGE_MB}MB and {settings.MAX_IMAGE_MB}MB"
)
return image
class AchievementsSerializer(AchievementsBaseSerializer):
class Meta(AchievementsBaseSerializer.Meta):
fields = "__all__"
Note: It is one the example. we have too many like this.
In this example i want to get all specialities with the list of doctors and doctor it self contain its achievements, place of works and many other models