优化没有。在django orm中为给定模型触发的查询

时间:2013-05-08 06:06:29

标签: python django profiling django-queryset django-orm

我有一个由用户上传的对象,它包含几个细节,但为了清楚起见,可以简单地通过以下模型表示来定义 -

在此之后,其他用户可以对该用户上传的内容进行upvote和downvote,从而投票模型

现在我想获得要在模板中显示的所有对象的upvotes和downvotes。因此,我向ObjectDetail类添加了两个函数,作为upvote和downvote。

这个模型的问题是,比如有20个对象,对于每个对象,2个查询被激活一个获得upvote而另一个获得downvote。因此,没有。对于20个对象,查询现在是40。

调整这个以减少查询数量并在每个对象上显示upvotes和downvotes的好方法是什么?

class ObjectDetail(models.Model):
    title = models.CharField()
    img = models.ImageField()
    description = models.TextField()
    uploaded_by = models.ForeignKey(User, related_name='voted_by')

    @property
    def upvote(self):
        upvote = Vote.objects.filter(shared_object__id = self.id, 
                             vote_type = True).count()
        return upvote

    @property
    def downvote(self):
        downvote = Vote.objects.filter(shared_object__id = self.id, 
                               vote_type = False).count()
        return downvote

class Vote(models.Model):
    vote_type = models.BooleanField(default = False)
    voted_by =  models.ForeignKey(User, related_name='voted_by')
    voted_for = models.ForeignKey(User, related_name='voted_for')
    shared_object = models.ForeignKey(ObjectDetail, null=True, blank=True)
    dtobject  = models.DateTimeField(auto_now_add=True)

2 个答案:

答案 0 :(得分:0)

https://docs.djangoproject.com/en/1.5/ref/models/querysets/#django.db.models.query.QuerySet.extra的文档中 我在这里使用extra()子句注入一些原始的sql。

编辑:这适用于名为“投票”且至少是Sqlite的应用。根据需要更改vot_*表名称。

from django.db.models import Count

objects = ObjectDetail.objects.all().extra(
  select={ 'upvotes': '''SELECT COUNT(*) FROM vot_vote
                          WHERE vot_vote.shared_object_id = vot_objectdetail.id
                            AND vot_vote.vote_type = 1''',
         'downvotes': '''SELECT COUNT(*) FROM vot_vote
                          WHERE vot_vote.shared_object_id=vot_objectdetail.id
                            AND vot_vote.vote_type = 0'''})

现在objects中的每个元素都有一个upvotes和downvotes属性。

答案 1 :(得分:0)

一方面,django确实为您提供了在必要时编写原始SQL的功能。但是这个例子很简单,你不必使用原始SQL来获取这些信息。

Django将推迟查询,直到您访问查询集的结果。因此,您可以尝试使用查询集和Q对象组合整个查询,然后访问组合查询的结果 - 这应该为所有结果触发一个数据库查询(或每个模型一个,而不是每个实例一个)。

那么,该怎么做?您希望获取给定的ObjectDetail记录集的所有投票记录。我假设你有一个ObjectDetail记录的id列表。

不幸的是,您的upvote和downvote属性会在其查询集上返回“count”的结果。这被视为“过滤器”调用产生的查询集的“访问结果”。我会更改这些方法定义以引用向后关系对象管理器vote_set,如下所示:

@property
def upvote(self):
    answer = 0
    for vote in self.vote_set.all ():
        if vote.vote_type:
            answer += 1
    return answer

@property
def downvote(self):
    answer = 0
    for vote in self.vote_set.all ():
        if not vote.vote_type:
            answer += 1
    return answer

注意我们只访问当前对象的查询集。在这个阶段,我们假设orm可以访问缓存的结果。

现在,在视图和/或模板中,我们想要组装大型复杂查询。

我的例子是功能性视图:

def home (request):

    # just assigning a constant list for simplicity.
    # Also was lazy and did 10 examples rather than 20.
    objids = [ 1, 5, 15, 23, 48, 52, 55, 58, 59, 60 ]

    # make a bunch of Q objects, one for each object id:
    q_objs = []
    for objid in objids:
        q_objs.append(Q(id__exact = objid))

    # 'or' them together into one big Q object.
    # There's probably a much nicer way to do this.
    big_q = q_objs[0]
    for q_obj in q_objs[1:]:
        big_q |= q_obj

    # Make another queryset that will ask for the Vote objects
    # along with the ObjectDetail objects.
    # Try commenting out this line and uncommenting the one below.
    the_objects = ObjectDetail.objects.filter(big_q).prefetch_related('vote_set')
    # the_objects = ObjectDetail.objects.filter(big_q)

    template = 'home.html'
    context = {
        'the_objects' : the_objects,
        }
    context_instance = RequestContext (request)
    return render_to_response (template, context, context_instance)

以下是相关文档的一些指示:

https://docs.djangoproject.com/en/1.5/topics/db/queries/#querysets-are-lazy https://docs.djangoproject.com/en/1.5/ref/models/querysets/#when-querysets-are-evaluated https://docs.djangoproject.com/en/1.5/topics/db/queries/#complex-lookups-with-q-objects https://docs.djangoproject.com/en/1.5/topics/db/queries/#following-relationships-backward