I have these two model below: models.py file:
class Profile(models.Model):
"""
User Profile Model
This model represents a user's profile information, including their avatar,
location, city, address, and geographical coordinates (latitude and longitude).
Fields:
- user (OneToOneField): The associated user instance.
- avatar (ImageField): An optional user avatar (profile picture).
- location_picture (ImageField): An optional picture of the user's location.
- city (CharField): An optional field for the user's city.
- address (CharField): An optional field for the user's address.
- latitude (DecimalField): An optional field for the latitude of the user's location.
Valid range: -90.0 to 90.0.
- longitude (DecimalField): An optional field for the longitude of the user's location.
Valid range: -180.0 to 180.0.
"""
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
verbose_name=_("User"),
help_text=_("the profile of a user instance."),
db_comment=_("the profile of a user instance."),
on_delete=models.CASCADE
)
city = models.CharField(
verbose_name=_("City"),
help_text=_("'Optional'.this is the city of the user."),
db_comment=_("'Optional'.this is the city of the user."),
max_length=255,
null=True,
blank=True
)
address = models.CharField(
verbose_name=_("Address"),
help_text=_("'Optional'.this is the address of a user."),
db_comment=_("'Optional'.this is the address of a user."),
max_length=255,
null=True,
blank=True
)
shair_location = models.BooleanField(
verbose_name=_("Shair Location"),
default=False,
help_text=_("'Optional.thie mean you want to shair your location or not!'"),
db_comment=_("'Optional.this mean you want to shair your location or not!'")
)
latitude = models.DecimalField(
verbose_name=_("Latitude"),
help_text=_("'Optional'.this is the latitude of a user location."),
db_comment=_("'Optional'.this is the latitude of a user location."),
max_digits=9,
decimal_places=6,
null=True,
blank=True,
validators=[
MinValueValidator(-90.0),
MaxValueValidator(90.0)
])
longitude = models.DecimalField(
verbose_name=_("Longitude"),
help_text=_("'Optional'.this is the longitude of a user location."),
db_comment=_("'Optional'.this is the longitude of a user location."),
max_digits=9,
decimal_places=6,
null=True,
blank=True,
validators=[
MinValueValidator(-180.0),
MaxValueValidator(180.0)
])
def __str__(self) -> str:
return f"user:{self.user}|city:{self.city}"
class Media(models.Model):
profile = models.ForeignKey(
Profile,
verbose_name=_("Profile"),
help_text=_("the profile can have lot of media(avatar or cover or resume...)"),
db_comment=_("the profile can have lot of media(avatar or cover or resume...)"),
on_delete=models.CASCADE,
related_name="medias"
)
media_type = models.IntegerField(
verbose_name=_("Media Type"),
choices=MediaType.choices,
help_text=_("this is the type of the media(avatar:0, resume:1, cover:2, gallery:3)"),
db_comment=_("this is the type of the media(avatar:0, resume:1, cover:2, gallery:3)"),
validators=[
MinValueValidator(0),
MaxValueValidator(3)
]
)
image = models.ImageField(
verbose_name=_("Image"),
help_text=_("this is the image field"),
db_comment=_("this is the image field"),
upload_to="images/%Y/%m/%d/",
validators=[
validate_image_size,
],
null=True,
blank=True
)
video = models.FileField(
verbose_name=_("Video"),
help_text=_("this is the video field"),
db_comment=_("this is the video field"),
upload_to="vidoes/%Y/%m/%d",
validators=[
validate_video_size,
],
null=True,
blank=True
)
created_at = models.DateTimeField(
verbose_name=_("Created At"),
help_text=_("this is the time the media is created"),
db_comment=_("this is the time the media is created"),
auto_now_add=True
)
and i have this serializer_class below for getting the avatar of a user and it's working but with two extra query for each profile in profiles list endpoint:
serializer.py:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
avatar = serializers.SerializerMethodField(method_name="get_avatar_url")
class Meta:
model = Profile
fields = ["id", "avatar", "user", "city", "address", "latitude", "longitude"]
def get_avatar_url(self, profile):
avatar_media = profile.medias.filter(media_type=0).first()
if avatar_media:
if avatar_media.image:
return avatar_media.image
else:
return None
else:
return None
media_type == 0 mean avatar
I want to get the image(avatar) of a profile and profiles(profiles endpoint: list of profile) but send two extra query to the database for each profile is there a way to optimize this code or perhaps change it?!
I finally figured out how to optimize this code after a lot of effort First, some changes in the get_serializer_context method in view:
def get_serializer_context(self):
if self.request.method == "GET":
if self.kwargs.get("pk") == None and self.kwargs.get("me") == None:
avatar_medias = Media.objects.select_related("profile").filter(profile__user=self.request.user, media_type=MediaType.AVATAR)
return {"request": self.request, "avatars_medias": avatar_medias}
Then we call the avatar medias field in ProfileSerializer:
class ProfileAdminSerializer(serializers.ModelSerializer):
user = UserSerializer()
plan = serializers.SerializerMethodField(method_name="get_plan")
avatar = serializers.SerializerMethodField(method_name="get_avatar_url")
class Meta:
model = Profile
fields = ["id", "avatar", "user", "city", "address", "latitude", "longitude", "plan"]
def get_avatar_url(self, profile):
avatar_medias = self.context.get("avatars_medias")
for media in avatar_medias:
if media.profile == profile:
if media.image:
return media.image.url
else:
return
This causes the query of the database, which was one query to the database for each profile, Now we get all the profiles with their avatars with a single query