Django 原始查询的执行次数超过预期

Django raw query being executed more times than expected

我有一个在 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' ) )

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

While a RawQuerySet instance can be iterated over like a normal QuerySet, RawQuerySet doesn’t implement all methods you can use with QuerySet. For example, bool() and len() are not defined in RawQuerySet, and thus all RawQuerySet instances are considered True. The reason these methods are not implemented in RawQuerySet is that implementing them without internal caching would be a performance drawback and adding such caching would be backward incompatible.

如果将结果转换为列表,将阻止附加查询:

categories = list(CategoryManager.get_by_popularity())

或者您可能希望 return 来自您的方法的列表:

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

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

但是 Django 不缓存原始查询集的结果。警告 in the docs 表示出于向后兼容性原因尚未添加缓存。

While a RawQuerySet instance can be iterated over like a normal QuerySet, RawQuerySet doesn’t implement all methods you can use with QuerySet. For example, __bool__() and __len__() are not defined in RawQuerySet, and thus all RawQuerySet instances are considered True. The reason these methods are not implemented in RawQuerySet is that implementing them without internal caching would be a performance drawback and adding such caching would be backward incompatible.