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))

処理を高速化し、サーバーの負荷を発生させないように、使用するクエリの数を最小限に抑える必要があります。

基本的に、私は3つのモデルを持っています:カテゴリー、著者、本。著者は(本ではなく)カテゴリに属しており、各カテゴリの下に5冊のランダムな本があるすべてのカテゴリのリストを取得する必要があります。

4

2 に答える 2

2

単一のクエリを好み、を使用している場合は、@Crazyshezyがコメントで提供している優れたリンクMySQLを確認してください。 バックエンドの場合、可能なクエリは次のとおりです( toとtoからのnull許容でない関係があると仮定):
PostgreSQLFKBookAuthorAuthorCategory

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 

次に、それをaでラップして、インスタンス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 = 250Book秒をランダムに生成しますが、1ページには多すぎるように見えるので、なぜだろうと思います。アイテムはタブなどで表示されていますか?おそらく、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ます。これは別のクエリを追加するだけです。

于 2013-01-01T16:20:06.150 に答える
1

問題の詳細とコンテキストがわからないため、これが役立つかどうかはわかりませんがorder_by('?')、特に一部のDBバックエンドでは、使用が非常に非効率的です。

少しランダムなエンティティを表示するために、カスタムフィルターを使用してこのアプローチを使用します。

@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 に答える