2012-02-16 15 views
1

私はPageというモデルを持っていますが、これにはPostが含まれています。私がしたいことは、すべてPageと、そのページの最新のPostを取得することです。 PagePostが指定されていない場合は、まだページが必要です。 (これはおなじみですか?これはSQLではLEFT JOINです)。ここでDjango集約クエリ

は、私が現在持っているものです。

Page.objects.annotate(most_recent_post=Max('post__post_time'))

これはのみPage秒を取得しますが、それはPost Sを得ることはありません。 Postもどうすれば入手できますか?

モデル:Following Links Backward

class Page(models.Model): 
    name = models.CharField(max_length=50) 
    created = models.DateTimeField(auto_now_add = True) 
    enabled = models.BooleanField(default = True) 

class Post(models.Model): 
    user = models.ForeignKey(User) 
    page = models.ForeignKey(Page) 
    post_time = models.DateTimeField(auto_now_add = True) 
+0

'Page'と' Post'関係は何ですか? – dm03514

+0

モデルを明瞭にするために追加されました – babonk

答えて

2

両者の関係に応じて、非常に簡単にfollow the relationshipsにできるはずですし、これを撮影select_related

を使用してパフォーマンスを向上させる:

class Page(models.Model): 
    ... 

class Post(models.Model): 
    page = ForeignKey(Page, ...) 

あなたは前方関係に従うことができます(つまり、:

Post.objects.select_related('page').all() 
を使用して効率的にすべて postsとその関連ページを取得する

これにより、すべてのページオブジェクトがプリフェッチされるクエリが1つ(より大きい)になります。

あなたはすべてのpagespostsそれらに関連を取得したい(あなたが持っているように)状況で、select_relatedは動作しません。できることの詳細については、thisthisthisの質問を参照してください。

+0

私は最新の投稿のみを取得しようとしています。 'page.post_set.all'はちょうどそれだけではなく、すべてを持っているようです。どのように私は希望のものを手に入れますか? – babonk

+0

'page.post_set.all()。latest()'またはテンプレートの 'page.post_set.all.latest'です。モデルの 'Meta'クラスに' get_latest_by'フィールドを指定する必要があります:https://docs.djangoproject.com/en/dev/ref/models/querysets/#latest –

+0

もしあなたがそのメソッドを使っているなら、 'most_recent_post = Max( 'post__post_time')'を得ることのポイントは何ですか? 'post_set.all.latest.post_time'から取得できますか? – babonk

1

おそらくあなたの最善の策は、ここではDjangoのドキュメントに記載された技術を使用することです。

あなたが後:これは最も効率的なソリューションである場合、私は知りませんが、これはだった

pages = Page.objects.annotate(most_recent_post=Max('post__post_time')) 
posts = [page.post_set.filter(post_time=page.most_recent_post) for page in pages] 

そしてポスト[0] [0]などのページの最新のポストを持っている必要があります解決策は、ジャンゴの左の結合の欠如について別の記事で述べた。

+0

これはうまくいきましたが、 – babonk

1

あなたは必要な最新Post列を持つと一緒にすべてのPageの列が含まれますdatabase viewを作成することができます。

CREATE VIEW `testapp_pagewithrecentpost` AS 
    SELECT testapp_page.*, testapp_post.* -- I suggest as few post columns as possible here 
    FROM `testapp_page` LEFT JOIN `testapp_page` 
    ON test_page.id = test_post.page_id 
    AND test_post.post_time = 
     (SELECT MAX(test_post.post_time) 
      FROM test_post WHERE test_page.id = test_post.page_id); 

は、その後、あなたは(manage.py syncが壊れないように)フラグmanaged = Falseでモデルを作成する必要があります。このアプローチではありませんしかし、

pages_with_recent_post = PageWithRecentPost.objects.filter(...) 
for page in pages_with_recent_post: 
    print page.name  # Page column 
    print page.post_time # Post column 

:あなたが最初に何を望むか行うことができます。これにより

class PageWithRecentPost(models.Model): # Or extend abstract BasePost ? 
    # Page columns goes here 
    # Post columns goes here 
    # We use LEFT JOIN, so all columns from the 
    # 'post' model will need blank=True, null=True 

    class Meta: 
     managed = False # Django will not handle creation/reset automatically 

ので、ちょうど1クエリで両方のテーブルからフェッチ:あなたは、列の重複を避けるためにinheritance from abstract Modelを使用することができます欠点フリー:

  • それはあなたがVIEWの作成SQLを追加する必要があります非常にDBエンジン固有
  • です
  • モデルが複雑な場合は、テーブルの列名の衝突を解決する必要があります。
  • データベースビューに基づくモデルは、読み取り専用になります(INSERT/UPDATEは失敗します)。
  • プロジェクトが複雑になります。複数のクエリを可能にすることは間違いなく簡単な解決方法です。
  • Page/Postの変更では、ビューを再作成する必要があります。