【问题标题】:Django ORM - MySQL query count optimization for related modelsDjango ORM - 相关模型的 MySQL 查询计数优化
【发布时间】:2018-04-03 03:06:36
【问题描述】:

我有两个相关的模型,如下所示:

class Enterprise(models.Model):
    id = models.AutoField(primary_key=True)
    subsystem_id = models.IntegerField()
    name = models.CharField(max_length=255, unique=True)
    modif_date = models.DateTimeField(auto_now=True)
    active = models.BooleanField(default=True)


class Project(models.Model):
    id = models.AutoField(primary_key=True)
    subsystem_id = models.IntegerField()
    name = models.CharField(max_length=255)
    modif_date = models.DateTimeField(auto_now=True)
    enterprise = models.ForeignKey('Enterprise'
    on_delete = CASCADE)
    active = models.BooleanField(default=True)

在我看来,需要获取所有活跃的企业并列出它们。我是这样做的:

enterprise_list = Enterprise.objects.annotate(project_count=Count('project')).filter(
    Q(active=True) | Q(subsystem_id=-1), project_count__gt=0
)

serializer = EnterpriseSerializer(enterprise_list, many=True)

然后,我的序列化程序显示项目列表和一些额外的查询:

class EnterpriseSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=255, required=False)
    project_list = serializers.SerializerMethodField()

    def get_project_list(self, enterprise):
        project_list = Project.objects.filter(Q(active=True) | Q(subsystem_id=-1),
                                              enterprise=enterprise)
        serializer = ProjectSerializer(project_list, many=True)
        return serializer.data

    class Meta:
        model = Enterprise
        fields = ('id', 'name', 'project_list')  

此代码运行良好,但存在非常严重的问题 - 性能。 Enterprise 的第一个查询返回 ~1500 个对象的列表。然后,对于每个对象,序列化程序都会执行单个查询以获取项目的额外数据,从而产生约 1500 个查询。

我尝试过prefetch_relatedselect_related,但要么我做错了,要么在我的情况下不起作用。

另一方面,我可以先获取项目列表。这可以消除我的计数注释。但我应该按企业对它们进行分组,但据我所知,Django ORM for MySQL 不支持此类操作。我不认为在 python 中解析数据并将其作为 dict 传递给序列化程序是一个好主意。

您能否给我一些提示,在我的情况下如何限制查询?也许prefetch/select_related 对我的情况会有所帮助,但是如何在这里正确使用它们呢?我正在使用 MySQL 数据库。

【问题讨论】:

    标签: mysql django orm django-rest-framework


    【解决方案1】:

    您可以通过以下方式使用prefetch_related

    from django.db.models import Prefetch
    
    enterprise_list = Enterprise.objects.annotate(project_count=Count('project')).filter(
        Q(active=True) | Q(subsystem_id=-1), 
        project_count__gt=0).prefetch_related(
            Prefetch('project_set', 
                queryset=Project.objects.filter(Q(active=True) | Q(subsystem_id=-1)), 
                to_attr='projects'
            )
        )
    
    serializer = EnterpriseSerializer(enterprise_list, many=True)
    

    在serializer.py中

    class EnterpriseSerializer(serializers.ModelSerializer):
        ...
    
        def get_project_list(self, enterprise):
            project_list = enterprise.projects
            serializer = ProjectSerializer(project_list, many=True)
            return serializer.data
    

    【讨论】:

    • 哦,所以我使用 prefetch_related 完全错误。感谢您的澄清。
    猜你喜欢
    • 2021-06-16
    • 1970-01-01
    • 2019-08-15
    • 2022-01-08
    • 1970-01-01
    • 2020-05-21
    • 2011-05-22
    • 2013-04-16
    • 2010-12-11
    相关资源
    最近更新 更多