2

外部キーごとに 1 つのオブジェクトのみを返すクエリセットを返すことは可能ですか?

たとえば、django_comments から最新のコメントを取得したいのですが、オブジェクトごとに 1 つのコメント (最新のコメント) だけが必要です。つまり、オブジェクトの最新のコメントのみを返し、そのオブジェクトの過去のすべてのコメントを除外します。これは、django_comments.content_type と django_comments.object_pk の sql group_by に似ていると思います。

++追加情報++

最終的な目標は、最新のコメントを持つスレッドによって表示/順序付けされたアクティブなコメント「スレッド」のリストを作成することです。これは、トピックが最近のアクティビティによってリストされている標準的なディスカッション掲示板と同じです。

これを行う最善の方法は、最新のコメントを取得し、それらをコンテンツ タイプと object_pk で並べ替えまたはグループ化して、関連するコンテンツ オブジェクトごとに 1 つのコメント (最新) のみが返されるようにすることだと思います。次に、そのコメントを使用して必要なすべての情報を取得できます。つまり、コメントを取得して pk をフォローしているだけなので、スレッドという単語は大まかに使用されます。

MODEL は django_threadedcomments で、これは django_comments を拡張し、ツリー、子、および親用のフィールドがいくつか追加されています。

見る:

...これは、親のすべてのインスタンスを含むすべてのコメントを返します

comments = ThreadedComment.objects.all().exclude(is_public='0').order_by("-submit_date")

...そしてこれは理想的です

comments = ThreadedComment.objects.all().exclude(is_public='0').order_by("submit_date").[plus sorting logic to exclude multiple instances of the same object_pk and content_type]

テンプレート:

{% for comment in comments %}

TITLE: {{comment.content_object.title}}

STARTED BY : {{comment.content_object.user}}

MOST RECENT REPLY : {{comment.user}} on {{comment.submit_date}}

{% endfor %}

再度、感謝します!

4

3 に答える 3

2

これを SQL で行うのはかなり難しいことです。おそらく、ORM を介してそれを行うことはできません。

これには GROUP BY を使用できません。これは、集計のために項目をグループ化する方法を SQL に指示するために使用されますが、ここでは行っていません。「SELECT x, y FROM table GROUP BY x」は、y の値が意味を持たないため、不正な SQL です。

明確なスキーマを念頭に置いてこれを見てみましょう。

CREATE TABLE objects ( id INTEGER PRIMARY KEY, name VARCHAR );
CREATE TABLE comments ( object_id INTEGER REFERENCES objects (id), text VARCHAR NOT NULL, date TIMESTAMP NOT NULL );

INSERT INTO objects (id, name) VALUES (1, 'object 1'), (2, 'object 2');
INSERT INTO comments (object_id, text, date) VALUES
   (1, 'object 1 comment 1', '2010-01-02'),
   (1, 'object 1 comment 2', '2010-01-05'),
   (2, 'object 2 comment 1', '2010-01-08'),
   (2, 'object 2 comment 2', '2010-01-09');

SELECT * FROM objects o JOIN comments c ON (o.id = c.object_id);

これを行うための最も洗練された方法は、Postgresql 8.4 のウィンドウ関数です。

SELECT * FROM (
    SELECT
        o.*, c.*,
        rank() OVER (PARTITION BY object_id ORDER BY date DESC) AS r
    FROM objects o JOIN comments c ON (o.id = c.object_id)
) AS s
WHERE r = 1;

これにより、各オブジェクトの最初のコメントが日付順に選択されます。新しいものから順に選択されます。これが何をしているのかわからない場合は、内部の SELECT を単独で実行し、rank() がどのように生成されるかを見てください。これにより、非常に簡単になります。

Postgresql でこれを行う他の方法は知っていますが、他のデータベースでこれを行う方法はわかりません。

これを動的に計算しようとすると、深刻な頭痛の種になる可能性があります。また、これらの複雑なクエリを適切に実行するには、さらに多くの作業が必要になります。last_comment_idオブジェクトごとにフィールドを保存し、コメントが追加または削除されたときにフィールドを更新して、結合してソートするだけの簡単な方法でこれを行う方がよいでしょう。おそらく SQL トリガーを使用して、この更新を自動的に処理できます。

于 2010-06-20T23:22:14.633 に答える
0

Glenn と vdboor に感謝します。提案されたアイデアは、SQL の複雑さを大幅に増大させ、パフォーマンスに深刻な影響を与えることに同意します。

last_comment_id の提案は非常に優れていますが、私の特定の状況では、コメントされた元のオブジェクトの content_type と object_pk 、およびオブジェクトの id とタイムスタンプを格納する別の "THREAD" モデルを作成するのが最善の方法だと思います。他のいくつかの中で、最後のコメント。これにより、単純なコンテンツ オブジェクトのルックアップと時系列でフィルター処理されたクエリセットが可能になり、フードの下で起こっていることがフロントエンドのプレゼンテーションをより厳密に反映するようになります。これはおそらく後世にとって良い考えです。:)

乾杯、

じゅん

于 2010-06-21T16:41:52.467 に答える
-1

最後の投稿を外部キーとしてどこかに保存することを検討してください(たとえば、親オブジェクトテーブル)。メッセージが投稿または削除されるたびに、このキーを更新してください。

はい、重複していますが、検討する価値があります。リクエスト(特にインデックスページ)ごとに複雑なクエリを実行する必要があると、アプリケーションのパフォーマンスが低下する可能性があります。これは、パフォーマンスを損なうことなく目的の効果を得る実用的な方法です。

于 2010-06-21T07:33:46.817 に答える