Django原始查询执行的次数比预期的多

时间:2016-05-20 14:04:49

标签: django django-orm

我在Django中完成了一个原始sql查询,并进行了测试以确保正在执行的查询数量减少到1.问题是我的迭代检查返回的对象会触发对该原始查询的重复调用。

class CategoryManager:
    ....

    @staticmethod
    def get_by_popularity():
        return Category.objects.raw( """.........""" )




class CategoryManagerTestCase( TestCase ):
    ....

    with self.assertNumQueries( 1 ):
        categories = CategoryManager.get_by_popularity( )

        for c in categories:
            if c.name == root_cat.name:
                self.assertEqual( c.visitors_count, 14 )

        #   when I add this second iteration the query gets executed a second time
        for c in categories:
            self.assertTrue( hasattr( c, 'id' ) )
            self.assertTrue( hasattr( c, 'name' ) )
            self.assertTrue( hasattr( c, 'parent_id' ) )
            self.assertTrue( hasattr( c, 'description' ) )
            self.assertTrue( hasattr( c, 'visitors_count' ) )
            self.assertTrue( hasattr( c, 'projects_count' ) )

2 个答案:

答案 0 :(得分:1)

不幸的是,原始查询是evaluated every time they are iterated

  

虽然RawQuerySet实例可以像普通法一样迭代   QuerySet,RawQuerySet不实现您可以使用的所有方法   查询集。例如,未定义 bool ()和 len ()   RawQuerySet,因此所有RawQuerySet实例都被认为是True。   这些方法没有在RawQuerySet中实现的原因是   在没有内部缓存的情况下实现它们将是一种表现   缺点和添加这样的缓存将是向后不兼容的。

答案 1 :(得分:1)

如果将结果转换为列表,则会阻止其他查询:

categories = list(CategoryManager.get_by_popularity())

或者您可能希望从您的方法返回一个列表:

@staticmethod
def get_by_popularity():
    return list(Category.objects.raw( """.........""" ))

如果查询是Category.objects.filter(...)而不是raw,那么多次循环遍历查询集只会导致它被提取一次。

然而,Django不会缓存原始查询集的结果。警告in the docs表明由于向后兼容性原因,未添加缓存。

  

虽然RawQuerySet实例可以像普通QuerySet一样进行迭代,但RawQuerySet并未实现您可以与QuerySet一起使用的所有方法。例如,__bool__()__len__()未在RawQuerySet中定义,因此所有RawQuerySet个实例都被视为True。这些方法未在RawQuerySet中实现的原因是,在没有内部缓存的情况下实现它们将是一个性能缺陷,并且添加此类缓存将是向后不兼容的。