2011-07-02 60 views
0

在我的Django應用程序之一,我要尋找的,可以用下面的例子來說明一個問題,一個優雅,高性能的解決方案的第一個X對象:如何檢索具有獨特的屬性值

鑑於這些對象:

class Author(models.Model): 
    name = models.CharField() 

class Book(models.Model): 
    collection = models.ForeignKey(Collection) 
    publication = models.DateField() 

class Collection(models.Model): 
    name = models.CharField() 
    author = models.ForeignKey(Author) 

我想檢索4(或任何其他小編號)最新出版的書籍,但我想也有4個不同的作者。這意味着如果2名最新出版的書籍是由同一作者,我只想得到一個在我的前4等留下3點對其他作者。

我曾經想過在多個步驟執行此操作,獲取最新的出版物,然後測試一招一式存儲筆者價值的,如果存在多個時,我會檢索到更多最新發布...但是這對正在做我的首頁,我需要這個代碼儘可能高效。

任何幫助將不勝感激。 感謝

+0

寫一個存儲過程,並從中檢索數據集... –

回答

0

你可以使用annotateextraraw。這裏是你如何使用annotatate

books = [a.book_set.latest('pub_date') for a in Author.objects 
        .annotate(latest=Max('book__pub_date')) 
        .order_by('-latest')[:5]] 

假設作者不必多本書籍與同pub_date你可以使用extra這樣的:

sql = '''SELECT MAX(app_book.pub_date) 
     FROM app_book 
     WHERE app_book.author_id=app_author.id''' 
latest = Author.objects.extra(
       select={'latest': sql}, 
       order_by=['-latest'])[:5].values_list('latest') 
books = Book.objects.filter(pub_date__in=[x[0] for x in latest]).order_by('-pub_date') 

如果使用raw你能抓住所有使用單個查詢書:

sql = '''SELECT * FROM app_book 
     WHERE app_book.pub_date IN 
      (SELECT MAX(app_book.pub_date) 
      FROM app_book 
      GROUP BY app_book.author_id) 
     ORDER BY app_book.pub_date DESC''' 
books = list(Book.objects.raw(sql)[:5]) 

我假設模型如下所示:

class Author(models.Model): 
    name = models.CharField(max_length=50) 

class Book(models.Model): 
    title = models.CharField(max_length=50) 
    author = models.ForeignKey(Author) 
    pub_date = models.DateTimeField() 

    class Meta: 
     get_latest_by = 'pub_date' 

爲了好玩,我想我應該基準三種方法(使用充滿100K左右僞書DB):

>>> %time annotate() 
(0.274) SELECT "app_author"."id", "app_author"."name", MAX("app_book"."pub_date") AS "latest" FROM "app_author" LEFT OUTER JOIN "app_book" ON ("app_author"."id" = "app_book"."author_id") GROUP BY "app_author"."id", "app_author"."name", "app_author"."id", "app_author"."name" ORDER BY "latest" DESC LIMIT 5; args=() 
(0.035) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."author_id" = 10 ORDER BY "app_book"."pub_date" DESC LIMIT 1; args=(10,) 
(0.036) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."author_id" = 9 ORDER BY "app_book"."pub_date" DESC LIMIT 1; args=(9,) 
(0.036) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."author_id" = 8 ORDER BY "app_book"."pub_date" DESC LIMIT 1; args=(8,) 
(0.036) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."author_id" = 7 ORDER BY "app_book"."pub_date" DESC LIMIT 1; args=(7,) 
(0.040) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."author_id" = 6 ORDER BY "app_book"."pub_date" DESC LIMIT 1; args=(6,) 
CPU times: user 0.32 s, sys: 0.15 s, total: 0.47 s 
Wall time: 0.47 s 
<<< [<Book: Susan>, <Book: Yasmin>, <Book: Carl>, <Book: Benny>, <Book: George>] 

>>> %time extra() 
(0.445) SELECT (SELECT MAX(app_book.pub_date) 
      FROM app_book 
      WHERE app_book.author_id=app_author.id) AS "latest" FROM "app_author" ORDER BY "latest" DESC LIMIT 5; args=() 
(0.045) SELECT "app_book"."id", "app_book"."title", "app_book"."author_id", "app_book"."pub_date" FROM "app_book" WHERE "app_book"."pub_date" IN (2038-11-25 11:33:30.425836, 2038-11-24 11:33:30.424598, 2038-11-23 11:33:30.423435, 2038-11-22 11:33:30.422227, 2038-11-21 11:33:30.421045) ORDER BY "app_book"."pub_date" DESC; args=(u'2038-11-25 11:33:30.425836', u'2038-11-24 11:33:30.424598', u'2038-11-23 11:33:30.423435', u'2038-11-22 11:33:30.422227', u'2038-11-21 11:33:30.421045') 
CPU times: user 0.32 s, sys: 0.18 s, total: 0.50 s 
Wall time: 0.50 s 
<<< [<Book: Susan>, <Book: Yasmin>, <Book: Carl>, <Book: Benny>, <Book: George>] 

>>> %time raw() 
(0.279) SELECT * FROM app_book 
      WHERE app_book.pub_date IN 
       (SELECT MAX(app_book.pub_date) 
       FROM app_book 
       GROUP BY app_book.author_id) 
      ORDER BY app_book.pub_date DESC; args=() 
CPU times: user 0.19 s, sys: 0.09 s, total: 0.28 s 
Wall time: 0.28 s 
<<< [<Book: Susan>, <Book: Yasmin>, <Book: Carl>, <Book: Benny>, <Book: George>] 
+0

總在你的代碼的SQL查詢的號碼是6,不是嗎?僅需2次查詢即可達到預期結果。 – dragoon

+0

@dragoon你會怎麼做?想到的另一種方式是使用'raw',這會導致一個查詢。 – zeekay

+0

見我的問題的鏈接,我同意這個方案,雖然有人聲稱解決方案不起作用:/ – dragoon

0

也許這篇文章回答你的問題

Django: Distinct foreign keys

+0

感謝disctinct()方法是很適應,但如文檔https://docs.djangoproject.com/en/1.2/ref/models/querysets/#django.db.models.QuerySet.distinct在mentionned它不應該可以與ORDER_BY查詢中使用,我敢肯定的,我需要一個... – Nielluin