Django - Слишком много одинаковых запросов

Я создаю приложение для рейтинга музыки и делаю сериализатор для альбомов, который имеет много отношений и один сериализатор метода агрегации, который, как я думаю, вызывает все проблемы. Метод получает среднее значение и количество рецензий для каждого альбома. Есть ли способ уменьшить количество запросов для повышения производительности?

Django Debug Toolbar View

Все мои модели

Вот как выглядит сериализатор альбомов.

"id": 2,
"title": "OK Computer",
"slug": "ok-computer",
"created_at": "2022-02-22T21:51:52.528148Z",
"artist": {
    "id": 13,
    "name": "Radiohead",
    "slug": "radiohead",
    "image": "http://127.0.0.1:8000/media/artist/images/radiohead.jpg",
    "background_image": "http://127.0.0.1:8000/media/artist/bg_images/radiohead.jpg",
    "created_at": "2022-02-22T00:00:00Z"
},
"art_cover": "http://127.0.0.1:8000/media/album/art_covers/ok-computer_cd5Vv6U.jpg",
"genres": [
    "Alternative Rock",
    "Art Rock"
],
"reviews": {
    "overall_score": null,
    "number_of_ratings": 0
},
"release_date": "1997-05-28",
"release_type": "LP",
"tracks": [
    {
        "position": 1,
        "title": "Airbag",
        "duration": "00:04:47"
    },
    {
        "position": 2,
        "title": "Paranoid Android",
        "duration": "00:06:27"
    }
],
"links": [
    {
        "service_name": "spotify",
        "url": "https://open.spotify.com/album/6dVIqQ8qmQ5GBnJ9shOYGE?si=L_VNH3HeSMmGBqfiqKiGWA"
    }
],
"aoty": null

Сериализатор альбомов с методом, который получает среднее значение и количество рецензий на альбом

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackSerializer(many=True, read_only=True)
    genres = StringRelatedField(
        source="album_genres", many=True, read_only=True)
    aoty = StringRelatedField(read_only=True)
    links = AlbumLinkSerializer(
        source="album_links", many=True, read_only=True)
    artist = SimpleArtistSerializer(source="artist_id")

    def get_avg_and_count_of_reviews(self, album: Album):
        reviews = Review.objects.only("rating").filter(album_id=album.id).aggregate(
            overall_score=Avg(F("rating"), output_field=IntegerField()), number_of_ratings=Count(F("rating"), output_field=IntegerField()))
        return reviews

    reviews = serializers.SerializerMethodField(
        method_name="get_avg_and_count_of_reviews")

    class Meta:
        model = Album
        fields = ["id",
                  "title",
                  "slug",
                  "created_at",
                  "artist",
                  "art_cover",
                  "genres",
                  "reviews",
                  "release_date",
                  "release_type",
                  "tracks",
                  "links",
                  "aoty"]

    # Save slug
    def create(self, validated_data):
        slug = slugify(validated_data["title"])
        return Album.objects.create(slug=slug, **validated_data)

Здесь находится кверисет в альбоме Viewset

class AlbumViewSet(ModelViewSet):
    queryset = Album.objects.prefetch_related("tracks").prefetch_related("album_genres").prefetch_related(
     "album_links").prefetch_related("reviews").select_related("aoty").select_related("artist_id").all()

Сначала вам нужно изменить ваш агрегат, который вы вызываете один раз для каждого альбома, на аннотацию, это устранит все эти дополнительные запросы агрегации

class AlbumViewSet(ModelViewSet):
    queryset = Album.objects.prefetch_related(
        "tracks",
        "album_genres",
        "album_links",
        "reviews"
    ).select_related(
        "aoty",
        "artist_id"
    ).annotate(
        overall_score=Avg(F("reviews__rating"), output_field=IntegerField()),
        number_of_ratings=Count(F("reviews__rating"), output_field=IntegerField())
    )

Теперь вы можете заменить ваше поле reviews двумя обычными IntegerFields

class AlbumSerializer(serializers.ModelSerializer):
    ...
    overall_score = serializers.IntegerField(source="overall_score")
    number_of_ratings = serializers.IntegerField(source="number_of_ratings")

Вернуться на верх