1

2つの別々のテーブルの結果をマージして最大値を取得しようとしています。投稿の最新のコメントを取得しようとしています。CommentからアクセスできるモデルがありますPost.commentsPostからアクセスできるとは別のボディもありますPost.body。コメントと本文の両方に、であるフィールドwhenがありDateTimeFieldます。最新のコメントまたは本文を含む投稿が最初に表示されるように、最新のアクティビティでソートされたクエリセットを返したいと思います。

モデルが次のようになっていると仮定します。

class Body(models.Model):
    when = models.DateTimeField(default=datetime.datetime.now)

class Post(models.Model):
    body = models.ForeignKey(Body)

class Comment(models.Model):
    post = models.ForeignKey(Post, related_name='comments')
    when = models.DateTimeField(default=datetime.datetime.now)

結果を処理し続け、さらに結果を制限するためにページネーターに渡すので、可能であれば結果をクエリセットのままにしておきたいと思います。

基本的に、私は電話できるようにしたいと思います:

q = Post.annotate(
    activity=Max(
        Union('comments__when', 'body__when')
    )
)
q = q.order_by('-activity')

しかし、私はその連合を達成する方法を知りません。

私が探しているものを実現するSQLは、次のものに匹敵すると思います。

SELECT
...
IF(MAX(c.`when`), MAX(c.`when`), b.`when`) AS `activity`
...
FROM `post` p
...
LEFT OUTER JOIN `comment` AS c
ON c.`post_id`=p.`id`
LEFT OUTER JOIN `body` AS b
ON p.`body_id`=b.`id`
...

このようなカスタマイズされた注釈と結合を実行できますか?

4

1 に答える 1

0

これを理解するのに非常に長い時間がかかりました。大きな問題の 1 つは、Django がステートメントのON節に対して複数の条件をサポートしていないことです。1 つのステートメントが 2 つの別個JOINの に依存しているため、テーブル名を正確に追跡するのに問題があります。django にテーブルに注釈を付けるように依頼すると、たとえば を使用すると、 T7 が一貫していないなどの条件になります。したがって、 Django によって自動的に生成された式を正確に生成する方法が必要でした。オンラインの多くの投稿では、クエリセットでサポートを使用するように指示されていますが、この場合、ORM を使用することによる多くの利点が失われます。SELECTJOINsMax()Max(T7.when) ... LEFT OUT JOIN table AS T7Max(T7.when)JOINs.raw()

私が思いついた解決策は、カスタム集計関数を生成することでした。私はこの関数を呼び出しましたCustomMax():

from django.conf import settings
from django.db import models

sum_if_sql_template = 'GREATEST(%(alt_table)s.%(alt_field)s, IFNULL(%(function)s(%(field)s), %(alt_table)s.%(alt_field)s))'

class CustomMaxSQL(models.sql.aggregates.Aggregate):
    sql_function = 'MAX'
    sql_template = sum_if_sql_template


class CustomMax(models.aggregates.Aggregate):
    name = 'Max'

    def __init__(self, lookup, **extra):
        self.lookup = lookup
        self.extra = extra

    def add_to_query(self, query, alias, col, source, is_summary):
        aggregate = CustomMaxSQL(col,
                             source=source,
                             is_summary=is_summary,
                             **self.extra)
        query.aggregates[alias] = aggregate

関数の使用法は次のとおりです。

q = Post.annotate(
    activity=CustomMax(
        'comments__when',
        alt_table="app_post",
        alt_field="when",
    )
)
q.query.join((
    'app_post',
    'app_comment',
    'id',
    'post_id',
))
q = q.order_by('-activity')

.join()alt_table が として存在できるようにを含めます。Django は、その部分のとステートメントJOINの両方の名前付けを自動的に処理します。この使用法から生成された SQL は、次のようなものです。SELECTJOINMax

SELECT 
...
GREATEST(app_post.when, IFNULL(MAX(T7.`when`), app_post.when)) AS `activity`,
...
`app_post`.`id`
...
INNER JOIN `app_post` ON ...
...
LEFT OUTER JOIN `app_comment` T7 ON (`app_post`.`id` = T7.`post_id`)
...

注: これは上記のPostおよびCommentモデル専用です。私の実際の実装はもう少し複雑で、このソリューションが必要でした。

joinDjango チームがこの提案されたパッチを開発して、次のステートメントを含めるようにしていれば、これもずっと簡単だったでしょう.extra(): https://code.djangoproject.com/ticket/7231

于 2012-05-30T10:22:29.427 に答える