Django ORM - 相关模型的MySQL查询计数优化

时间:2017-10-22 13:24:11

标签: mysql django orm django-rest-framework

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

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,但要么我做错了,要么我的情况不起作用。

另一方面,我可以先获得项目清单。这可以消除我的计数注释。但我应该按企业分组,但据我所知,MySQL的Django ORM并不支持这样的操作。我不认为在python中解析数据并将其传递给序列化器,因为dict是个好主意。

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

1 个答案:

答案 0 :(得分: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