【问题标题】:Django Rest FrameWork Reduce number of queries using group byDjango Rest FrameWork 使用 group by 减少查询次数
【发布时间】:2021-05-28 04:25:49
【问题描述】:

我正在使用 Django Rest 框架编写一个 api。该 api 获取客户列表。客户有许多项目。我的 api 应该返回包含已完成、待处理和总计项目数量的客户列表。我的 api 工作,但它有太多的 sql 查询。 api是分页的

class ClientViewSet(ModelViewSet):
    """
     A simple view for creating clients, updating and retrieving
    """
    model = Client
    queryset = Client.objects.all()
    serializer_class = ClientSerializer

现在我的客户端序列化器


class ClientSerializer(serializers.ModelSerializer):
    total_projects_count = serializers.SerializerMethodField()
    on_going_projects_count = serializers.SerializerMethodField()
    completed_projects_count = serializers.SerializerMethodField()

    class Meta:
        model = Client
        fields  = __all__

    def get_total_projects_count(self, obj):
        return obj.total_projects_count()

    def get_on_going_projects_count(self, obj):
        return obj.on_going_project_count()

    def get_completed_projects_count(self, obj):
        return obj.completed_projects_count()

项目有一个客户端外键。我尝试使用注释来获取如下所有产品并进行分组。但是 annotate 只对单个字段起作用。

projects = Project.objects.filter(client__in=queryset).values('client', 'status')

如何对多个字段进行分组并将该额外参数传递给序列化程序。或者有没有更好的方法。我也尝试了 prefetch_related 但 total_projects_count 仍在执行新的 sql 查询

【问题讨论】:

  • 可以添加Client模型的三种计数方式吗?

标签: django postgresql django-rest-framework django-orm drf-queryset


【解决方案1】:

您需要注释查询集中的计算字段,然后使用注释列返回相关结果,而不是调用方法。这将确保使用单个查询检索所有数据,这肯定会更快。

  1. 更新您的查询集。
class ClientViewSet(ModelViewSet):
    """
     A simple view for creating clients, updating and retrieving
    """
    model = Client
    queryset = Client.objects.annotate(total_projects_count_val=...)
    serializer_class = ClientSerializer
  1. 然后,在您的序列化程序中,使用带注释的列
class ClientSerializer(serializers.ModelSerializer):
    total_projects_count = serializers.SerializerMethodField()
    on_going_projects_count = serializers.SerializerMethodField()
    completed_projects_count = serializers.SerializerMethodField()

    class Meta:
        model = Client
        fields  = __all__

    def get_total_projects_count(self, obj):
        return obj.total_projects_count_val

    ...

看方法名,我想你需要Case-When注解。

【讨论】:

  • 项目是不同的表
【解决方案2】:

我使用以下查询减少了查询

from django.db.models import Count, Q

pending = Count('project', filter=Q(project__status="pending"))
finished = Count('project', filter=Q(project__status="finished"))
queryset = Client.objects.annotate(pending=pending).annotate(finished=finished)

现在可以访问 queryset[0].finished 等。当我使用分页提供的 drf 时,生成的查询是

SELECT "clients_client"."id",
       "clients_client"."created_at",
       "clients_client"."updated_at",
       "clients_client"."client_name",
       "clients_client"."phone_number",
       "clients_client"."email",
       "clients_client"."address_lane",
       "clients_client"."state",
       "clients_client"."country",
       "clients_client"."zipCode",
       "clients_client"."registration_number",
       "clients_client"."gst",
       COUNT("projects_project"."id") FILTER (WHERE "projects_project"."status" = 'pending') AS "pending",
       COUNT("projects_project"."id") FILTER (WHERE "projects_project"."status" = 'finished') AS "finished"
  FROM "clients_client"
  LEFT OUTER JOIN "projects_project"
    ON ("clients_client"."id" = "projects_project"."client_id")
 GROUP BY "clients_client"."id"
 ORDER BY "clients_client"."id" ASC
 LIMIT 6

【讨论】:

    猜你喜欢
    • 2019-05-20
    • 2021-08-01
    • 2021-04-05
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-10
    相关资源
    最近更新 更多