about 2 years ago

用select_related来提取数据和不用select_related来提取数据的差别。

我有一个Model(用户每天会填写一些问题,这个Model用来收集用户每天填写的问题的答案):

class Answer(models.Model):
    day_id = models.IntegerField()
    set_id = models.IntegerField()
    question_id = models.IntegerField()
    answer = models.TextField(default='')
    user = models.ForeignKey(User)

    def __unicode__(self):
        result = u'{4}:{0}.{1}.{2}: {3}'.format(self.day_id, self.set_id, self.question_id, self.answer, self.user.id)
        return result

这个Model除了自带的Field外还有一个外键 user,用于引用Django自带的User表。

我需要将所有用户的每天的答案都输出到一个文件里, 所以首先要提取出所有的答案:

answers = Answer.objects.all()

for answer in answers:
    print answer

运行这个指令会对数据库进行981次请求。数据库里有980行数据,Answer.objects.all()算是一次请求。那么剩下的980次请求是在干嘛呢?问题出在:

result = u'{4}:{0}.{1}.{2}: {3}'.format(self.day_id, self.set_id, self.question_id, self.answer, self.user.id)

当打印answer这个Object的时候,除了会打印出自身的Field外,还会打印出user.id. 由于Answer这个表中没有user.id这个Field,所以要单独请求User的表给出相应的id。这个就是造成980次请求的原因。每次打印一个answer,都要请求一遍User表来获取相对应的用户id。

这种提取数据的方式明显是不合理的,为了能一次完成任务,可以使用select_related:

answers = Answer.objects.all().select_related('user')

for answer in answers:
    print answer

这样,数据库请求只有一次。select_related()的作用其实是把Answer和User这两张表给合并了:

SELECT ... FROM "answer" INNER JOIN "auth_user" ON ( "answer"."user_id" = "auth_user"."id" )

这样页面的载入时间就会被大大减少。

← 在Linux下设置重复执行的工作 更好的编写Python代码的方式 →
 
comments powered by Disqus