此代码目前正在执行大约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本随机书籍。
答案 0 :(得分:2)
如果您更喜欢单个查询并且正在使用MySQL
,请在评论中查看@Crazyshezy提供的the excellent link。
对于PostgreSQL
后端,可能的查询是(假设从FK
到Book
以及从Author
到Author
存在不可为空的Category
关系):
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
然后,您可以将其包含在RawQuerySet
内以获取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 = 250 Book
s,我只是想知道为什么因为单个页面看起来太多了。项目是否显示为标签或其他内容?也许您可以通过执行Ajax来减少SQL的数量,或者简化需求?
更新
要使用book.author
而不是触发其他查询,请尝试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
。这只是添加了另一个查询。
答案 1 :(得分: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
组的随机类别。
这不是完美的解决方案,但希望它有所帮助。