1

与えられた 2 つのモデル:

class Post(models.Model):
   ...

class Comment(models.Model):
   post = models.ForeignKey(Post)
   ...

一連の投稿で最後の 3 つのコメントを取得する良い方法はありますか (つまり、投稿ごとに 1 回ではなく、DB への 1 回の往復で)。私が何を意味するかを示す単純な実装:

for post in Post.objects.filter(id__in=some_post_ids):
  post.latest_comments = list(Comment.objects.filter(post=post).order_by('-id')[:3])

を指定するとsome_post_ids == [1, 2]、上記は 3 つのクエリになります。

[{'sql': 'SELECT "myapp_post"."id" FROM "myapp_post" WHERE "myapp_post"."id" IN (1, 2)', 'time': '0.001'},

{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 1  LIMIT 3', 'time': '0.001'},

{'sql': 'SELECT "myapp_comment"."id", "myapp_comment"."post_id" FROM "myapp_comment" WHERE "myapp_comment"."post_id" = 2  LIMIT 3', 'time': '0.001'}]
4

1 に答える 1

1

Djangoのドキュメントから:

スライス。QuerySet の制限で説明されているように、QuerySet は Python の配列スライス構文を使用してスライスできます。未評価の QuerySet をスライスすると、通常、別の未評価の QuerySet が返されますが、スライス構文の「step」パラメーターを使用すると、Django はデータベース クエリを実行し、リストを返します。(部分的または完全に) 評価された QuerySet をスライスしても、リストが返されます。

あなたの単純な実装は正しく、DB クエリを 1 つだけ作成する必要があります。ただし、それを呼び出さないでくださいlist。DBがすぐにヒットする原因になると思います(ただし、単一のクエリのみである必要があります)。クエリセットはすでに反復可能であり、呼び出す必要はありませんlistlist同じドキュメント ページからの呼び出しの詳細:

リスト()。list() を呼び出して QuerySet の評価を強制します。例: entry_list = list(Entry.objects.all()) ただし、Django はリストの各要素をメモリにロードするため、これには大きなメモリ オーバーヘッドが発生する可能性があることに注意してください。対照的に、QuerySet を反復処理すると、データベースを利用してデータをロードし、必要なときにのみオブジェクトをインスタンス化します。

更新

追加の説明により、次のように動作するはずです(ただし、テストされていないため、報告してください!):

post.latest_comments = Comment.objects.filter(post__in=some_post_ids).order_by('-id')

確かに、投稿ごとに 3 つのコメントという制限はありません。それは可能だと確信していますが、頭の中で構文を考えることはできません。また、最適化を向上させるために、任意のモデルに対していつでも手動クエリを実行できることを忘れないでください。Comment.query("Select ...;")

ここで「グループから上位 N を選択する」問題に関する情報を考えると、IN句が少数の投稿になる場合は、a) 複数のクエリを実行するか、b) 投稿のすべてのコメントを選択する方が安くなる可能性があります。次に、Python でフィルタリングします。多数のコメントを含む少数の投稿の場合は a を使用し、投稿ごとのコメントが比較的少ない場合は b を使用することをお勧めします。

于 2013-03-25T20:10:47.100 に答える