防止在SerializerMethodField中重复查询

时间:2019-03-07 09:32:29

标签: django django-rest-framework django-serializer

所以我叫我的序列化器是这样的:

result_serializer = TaskInfoSerializer(tasks, many=True)

和序列化器:

class TaskInfoSerializer(serializers.ModelSerializer):
    done_jobs_count = serializers.SerializerMethodField()
    total_jobs_count = serializers.SerializerMethodField()
    task_status = serializers.SerializerMethodField()

    class Meta:
        model = Task
        fields = ('task_id', 'task_name', 'done_jobs_count', 'total_jobs_count', 'task_status')

    def get_done_jobs_count(self, obj):
        qs  = Job.objects.filter(task__task_id=obj.task_id, done_flag=1)
        condition = False
        # Some complicate logic to determine condition that I can't reveal due to business
        result = qs.count() if condition else 0
        # this function take around 3 seconds
        return result

    def get_total_jobs_count(self, obj):
        qs = Job.objects.filter(task__task_id=obj.task_id)
        # this query take around 3-5 seconds
        return qs.count()

    def get_task_status(self, obj):
        done_count    = self.get_done_jobs_count(obj)
        total_count    = self.get_total_jobs_count(obj)
        if done_count >= total_count:
            return 'done'
        else:
            return 'not yet'

当调用get_task_status函数时,它将调用其他2个函数,并再次进行这2个代价高昂的查询。 有什么最好的方法来防止这种情况吗?而且我真的不知道这些函数的调用顺序,它是基于Meta字段中声明的顺序吗?还是在那之上?

编辑: get_done_jobs_count中的逻辑有点复杂,在获取任务时,我无法使其成为单个查询

修改2: 我只是将所有这些count函数带入模型并使用cached_property https://docs.djangoproject.com/en/2.1/ref/utils/#module-django.utils.functional 但这又提出了一个问题:这个数字可靠吗?我对django缓存了解不多,是cached_property仅存在于此实例(直到API get任务列表返回响应)还是存在一段时间?

3 个答案:

答案 0 :(得分:1)

我只是尝试使用cached_property,它确实解决了问题。

型号:

from django.utils.functional import cached_property
from django.db import models

class Task(models.Model):
    task_id = models.AutoField(primary_key=True)
    task_name = models.CharField(default='')

    @cached_property
    def done_jobs_count(self):
        qs  = self.jobs.filter(done_flag=1)
        condition = False
        # Some complicate logic to determine condition that I can't reveal due to business
        result = qs.count() if condition else 0
        # this function take around 3 seconds
        return result

    @cached_property
    def total_jobs_count(self):
        qs = Job.objects.filter(task__task_id=obj.task_id)
        # this query take around 3-5 seconds
        return qs.count()

    @property
    def task_status(self):
        done_count    = self.done_jobs_count
        total_count    = self.total_jobs_count
        if done_count >= total_count:
            return 'done'
        else:
            return 'not yet'

序列化器:

class TaskInfoSerializer(serializers.ModelSerializer):

    class Meta:
        model = Task
        fields = ('task_id', 'task_name', 'done_jobs_count', 'total_jobs_count', 'task_status')

答案 1 :(得分:0)

您可以注释这些值,以避免进行额外的查询。因此,传递给序列化程序的queryset看起来像这样(它可能会根据您使用的Django版本和作业的相关查询名称而变化):

#include <iostream>
using namespace std;

int main()
{
    float x, z, a;
    a = x + z;



    cout << "Welcome to the calculator" << endl;
    cout << "State the first number " << endl;
    cin >> x ;
    cout << "State the second number " << endl;
    cin >>  z ;
    cout << "If you wanted to time number" << x << "by this number" << z << "The result would be : " << a << endl;



    system("pause");
    return 0;
} 

然后序列化器方法如下:

tasks = tasks.annotate(
    done_jobs=Count('jobs', filter=Q(done_flag=1)),
    total_jobs=Count('jobs'),
)
result_serializer = TaskInfoSerializer(tasks, many=True)

编辑:如果必须为每个任务实例调用该方法,那么cached_property将无济于事(似乎是这种情况)。问题不在于计算,而在于您是否必须为每个单独的任务访问数据库。您必须集中精力在单个查询中获得计算所需的所有信息。如果那不可能或太复杂,也许可以考虑更改数据结构(模型)以便于实现。

答案 2 :(得分:-1)

使用 iterator()并计算Iterator可能会解决您的问题。

job_iter = Job.objects.filter(task__task_id=obj.task_id).iterator()
count = len(list(job_iter))
return count

如果需要,可以使用select_related()和prefetch_related()一次检索所有内容。

注意 :如果使用 iterator()运行查询,则 prefetch_related()通话将被忽略

您可能想浏览optimisation的文档