7

此代码当前正在执行大约 50 个 SQL 查询:

c = Category.objects.all()

categories_w_rand_books = []

for category in c:
    r = Book.objects.filter(author__category=category).order_by('?')[:5]

    categories_w_rand_books.append((category, r))

我需要将使用的查询数量减少到最低限度以加快速度并且不会导致服务器负载。

基本上,我有三个模型:类别、作者、书籍。作者属于类别(不是书籍),我需要获取所有类别的列表,每个类别下有 5 本书。

4

2 回答 2

2

如果您更喜欢单个查询并且正在使用MySQL,请查看@Crazyshezy 在他的评论中提供的出色链接。
对于后端,可能的查询是(假设从to和 from toPostgreSQL有不可为空的FK关系):BookAuthorAuthorCategory

SELECT * FROM (
    SELECT book_table.*, row_number() OVER (PARTITION BY category_id ORDER BY RANDOM()) AS rn 
    FROM book_table INNER JOIN author_table ON book_table.author_id = author_table.id
) AS sq 
WHERE rn <= 5 

然后,您可以将其包装在 aRawQuerySet中以获取Book实例

from collections import defaultdict
qs = Book.objects.raw("""The above sql suited for your tables...""")
collection = defaultdict(list)
for obj in qs:
    collection[obj.category_id].append(obj)

categories_w_rand_books = []
for category in c:
    categories_w_rand_books.append((category, collection[category.id]))

您可能不想在没有缓存的情况下直接为每个请求运行此查询。

此外,您的代码最多随机生成 50*5=250Book秒,我只是想知道为什么,因为单页看起来太多了。项目是否显示为选项卡或其他内容?也许您可以通过执行 Ajax 来减少 SQL 的数量,或者简化需求?

更新

要使用book.authorw/o 触发多于另一个查询,请尝试prefetch_related_objects

from django.db.models.query import prefetch_related_objects
qs = list(qs) # have to evaluate at first
prefetch_related_objects(qs, ['author'])
# now instances inside qs already contain cached author instances, and
qs[0].author # will not trigger an extra query

上面的代码批量预取作者并将它们填充到qs. 这只是添加了另一个查询。

于 2013-01-01T16:20:06.150 回答
1

我不确定这是否会对您有所帮助,因为我不知道您的问题的详细信息和上下文,但是使用order_by('?')效率非常低,特别是对于某些数据库后端。

为了显示具有一点随机性的实体,我使用这种方法,使用自定义过滤器:

@register.filter
def random_iterator(list, k):
    import random
    class MyIterator:
        def __init__(self, obj, order):
            self.obj=obj
            self.cnt=0
            self.order = order

        def __iter__(self):
            return self

        def next(self):
            try:
                result=self.obj.__getitem__(self.order[self.cnt])
                self.cnt+=1
                return result
            except IndexError:
                raise StopIteration

    if list is None:
        list = []
    n = len(list)
    k = min(n, k)
    return MyIterator(list, random.sample(range(n), k))

我的 Django 视图中的代码是这样的:

RAND_BOUND = 50    
categories = Category.objects.filter(......)[RAND_BOUND]

而且,我以这种方式在我的模板中使用它:

{% for cat in categories|random_iterator:5 %}
 <li>{{ cat }}</li>             
{% endfor %}

此代码将选择 5 个随机类别的(减少的)集合RAND_BOUND。这不是完美的解决方案,但希望它有所帮助。

于 2012-12-31T15:31:10.367 回答