2011-05-25 67 views
1

所以,我有一個人模型,然後我有一個模型名稱Carusage。 Carusage的相關部分是這樣的:檢索最新的對象

class Carusage(models.Model): 
    person = models.ForeignKey(Person) 
    start = models.DateTimeField() 
    end = models.DateTimeField(null=True, blank=True) 

一個人可以坐車,然後系統會創建一個新的Carusage實例,並開始爲當前時間保存它。然後當人返回汽車時,當前時間被保存到結束。

現在,在我的代碼中,我有一個Person模型列表,並且我想要爲每個Person檢索Carusage中的最新日期。所以如果一個人剛剛返回了賽車,我想讓最新的Carusage的尾場與那個人聯繫起來,如果這個人仍然擁有賽車,那麼我想要開始賽場。

最好我想在一個SQL語句中這樣做,因爲我的人員列表可能會變得相當大(下界~10,上界〜10.000)。我試過這樣的事情:

Carusage.objects.filter(person__in(person_list)).exclude(start__gte(time_now)) 

然後想着註釋,但想不出我會如何繼續。

所以目前我在做這個:

time_now = datetime.datetime.now() 
time_list = [] 
for p in person_list: 
    latest = Carusage.objects.filter(person=p).exclude(start__gte=time_now).only('start', 'end').latest('start') 
    try: 
     if latest.end<time_now: 
      time=latest.end 
     else: 
      raise 
    except: 
     time=latest.start 
    time_list.append(time) 

顯然,我的代碼運行的方式來減緩(500人名單約5秒)。運行這些/這些查詢的「django-way」是什麼? 我想實現的兩件事:只有一次爲Carusage命中數據庫(至少不是len(person_list)次),並且只從數據庫中獲取相關時間(只需要最新的時間...)。 有什麼辦法可以做到這一點?

回答

2

你確實有,你連同聯盟將兩個單獨的結果。

  1. 返回的汽車。開始和結束時間。每人有(可能)多輛汽車,而你只需要其中一輛汽車。即使在純SQL中,這也是一個相當複雜的查詢,需要HAVING子句並導致(可能)性能下降。

  2. 汽車尚未返回。

通常情況下,如果您有兩個單獨的規則,您會更快樂地進行兩個單獨的查詢。

您實際上正在對CarUsage進行聚合,該聚合由具有等於組的最大值的開始(或結束)的Person組成。

returned = Person.objects.filter(carusage__end__isnull=True).annotate(Max('carusage__start')) 
not_returned = Person.objects.filter(carusage__end__isnull=False).annotate(Max('carusage__end')) 

我想這就是你要找的。

+0

我可以看到那裏的邏輯,我認爲這適用於我的sqlite測試數據庫。但是,當我嘗試在使用SQLServer和pyodbc的生產環境中使用它時,出現此錯誤:「文本,ntext和圖像數據類型無法進行比較或排序,除非使用IS NULL或LIKE運算符。 更改數據庫架構不是一個選項,所以我認爲我只是用這個舊的原始sql代碼去... :) – 2011-05-25 16:02:35

+1

@SindriGuðmundsson:「我想我只會用傳統的原始數據這一個SQL代碼「。壞的,糟糕的政策。您的數據模型不僅包含您在問題中顯示的內容。這意味着您可能必須使用'aggregate'而不是'annotate'來生成一些選定的字段 - 以避免圖像,文本和ntext字段。 – 2011-05-25 17:14:20

+0

總計只會給我一個日期。從這兩個陳述中,用聚合替換註釋我只會得到兩個日期(一個是最近一次回來的汽車,另一個是最近一次沒有返回的汽車)。澄清我需要每個人一個日期時間對象。或者在你的回答中有什麼我不明白的地方? – 2011-05-26 09:49:43

2

通過以這種方式使用異常,可以保證得到速度慢的代碼。例外情況非常昂貴,並且potentially slow down your code a lot(例外情況下高達5-10倍)。除了提高和捕捉異常外,只需使用else:

if latest.end<time_now: 
    time=latest.end 
else: 
    time=latest.start 

也可以簡單地使用Django的order_by過濾器嗎?例如,爲了讓所有「Carusages」由起始值定人命令,調用:

Carusage.objects.filter(person=p).order_by('-start').all() 
+0

我真的沒有想到這個try-except從句。刪除它,代碼運行得更快,但只是稍微。您提出的查詢仍需要將數據庫打到N次。雖然你的回答稍微加快了我的代碼,但它仍然無法幫助我嘗試儘可能少地嘗試訪問數據庫。 +1的升級速度。 – 2011-05-25 13:25:20