2016-08-18 52 views
0

我有以下Python代碼:Django的查詢集。去年()方法返回不正確元件

models.py 
class Person(models.Model): 
    first_name = models.CharField(max_length=32, null=True, blank=True) 
    last_name = models.CharField(max_length=64, null=True, blank=True) 
    order = models.PositiveSmallIntegerField(null=True, blank=True) 

我有兩個人分別加入作爲Persons名爲「人1」和「人2」,。他們都具有1

views.py 
def get_people(): 
    people = Person.objects.order_by('order') 
    print(people) 
    for p in people: 
     print(p) 
     if p == people.last(): 
      print ('Last Person') 

相同的順序這裏的結果:

>>> get_people() 
[<Person: Person 1>, <Person: Person 2>] 
<Person 1> 
u'Last Person' 
<Person 2> 

花了一點挖,但我發現這個結果和根本原因。

>>> people = Person.objects.order_by('order') 
>>> print(people) 
[<Person: Person 1>, <Person: Person 2>] 
>>> print(people.first()) 
<Person 1> 
>>> print(people.last()) 
<Person 1> 
>>> people.first() == people.last() 
True 
>>> people[0] 
<Person 1> 
>>> people[1] 
<Person 2> 

我看了看源代碼,它出現在最後一個()方法只是運行reverse()與我選擇相同的排序。由於這兩個元素具有相同的順序號1,所以反方法返回與原始列表完全相同的列表,假設因爲當對逆向排序時,同樣的規則適用於在一個領帶中具有最低記錄ID的元素是第一個,而是真正扭轉已經檢索的列表。我不明白他們爲什麼不只是取得已經檢索到的元素列表並從索引中獲取最後一個元素。我嘗試使用[-1]負指數來得到它,但是沒有實現並引發異常。

那麼有人可以解釋爲什麼這樣編碼嗎?如果您的某些元素與所訂購的房產共享相同的價值,則可能會產生問題。特別是如果通過對last()的後續調用來多次訪問queryset。這是爲了表現還是我沒有看到其他問題?相反,在這個用例使用最後的()方法,我只是在做這種比較,而不是:

if p == people[len(people) - 1]: 

這工作。在這種情況下,我知道這些人並不是空的,所以我們不會得到IndexError - 如果它是空的,代碼將永遠不會在循環中執行。一般情況下可能是:

l = len(people) 
return None if l == 0 else return people[l -1] 

或者:

try: 
    l = len(people) 
    return people[l - 1] 
except IndexError: 
    return None 

你能分享這種行爲的一些見解,請? Django文檔中唯一說明last()方法與first()類似,但返回查詢集的最後一個元素。在這種情況下,它不像描述的那樣運行。這種行爲讓我感到困惑。我認爲它只是從當前列表中取出最後一個元素,而不是創建一個新的反向列表並獲取第一個元素。

在此先感謝...

+1

你爲什麼不定義一個更好的排序?你有它的方式他們是一個領帶,所以這裏的第一個和最後一個想法是沒有意義的.. – wim

+0

這只是一個例子。人們在添加時應該被正確地命令,但不知怎的,他們不知所措。在可能具有相同記錄的多個記錄的字段上進行訂購時,有許多實際示例。說出生日期。 – Furbeenator

回答

2

如果有人對這個邊緣案例有任何想法,其原因很可能是一致性和性能的組合。

首先,您通常無法評估整個查詢集以獲取最後一個元素而不會造成巨大的性能損失。 Person.objects.order_by('order').last()應該得到一行,而不是整個表 - 可能包含數百萬行。因此,如果未經評估的查詢集,則需要顛倒SQL中的順序並獲取頂部元素。這將永遠受到你描述的問題的困擾。

只有在計算queryset時,纔可以獲取緩存中的最後一個元素,但這意味着您會得到不一致的結果。看看下面的代碼:

people = Person.objects.order_by('order') 
p1 = people.last() 
bool(people) 
p2 = people.last() 

在你的榜樣,p1<Person 1>。但是,如果在計算查詢集時採用高速緩存的最後一個元素,則p2會突然變爲<Person 2>,這是因爲緩存已填滿。這種自我矛盾使得開發者的工作非常困難。

雖然這可能不是很直觀,但它是在實際數據庫查詢中翻譯.last()方法並獲得可接受的性能和自洽結果的最佳方法。無序或部分排序的結果集有一個未定義的順序(甚至可能在查詢之間任意改變)的事實是SQL的一個很好理解的方面,因此總的來說這是least astonishment的路徑。

+0

啊哈。得到它了。感謝您的澄清。 – Furbeenator

1

相信隨着代碼的問題是,你以升序排列留給了Django的弄清楚如何處理兩者之間的決勝局。在SQL中你在你的get_people()方法寫的等效如下:

SELECT * FROM Person ORDER BY order ASC

所以在那裏是兩個人都用相同的「秩序」價值的情況下,您的結果將永遠不會回來了正確。相反,你想要一個看起來更類似於這樣的查詢:

SELECT * FROM Person ORDER BY order, last_name, first_name(假設你想在排序後按姓氏排序。

我遇到了一個像我這樣設計的應用程序的問題,解決方案非常簡單。相反擊敗你的頭試圖找出潛在的「問題」與Django的API(儘管實際上這只是作爲表的設計智能),你可以使用這樣的事情:

views.py 
def get_people(): 
    people = Person.objects.order_by('order', 'last_name', 'first_name') 
    print(people) 
    for p in people: 
     print(p) 
     if p == people.last(): 
      print ('Last Person') 

公告中我們通過Django「創建查詢」的那一行包括多列。這將解決你的領帶問題,所以如果兩個人有相同的順序,它會按姓氏排序。

+0

是的,我理解並感謝您的回答。在這種情況下,我只想通過訂單字段進行訂購,並且在很多情況下您不希望進行額外的訂購,但我想您可以添加ID或其他內容。儘管如此,對於我所寫的每個查詢集都必須這樣做,我似乎很愚蠢。我更加好奇他們爲什麼選擇以這種方式寫出來,或者如果它是隨意的。似乎對我來說反轉列表的效率較低,而不僅僅是獲取長度並獲得列表末尾的元素。 – Furbeenator

+0

不幸的是,這可能是你不得不問的開發者。我會假設他們給出的答案可能類似,因爲他們會說這是「你正在排序的問題」,而不是他們的「我們如何排序」問題。請記住,您在Django中構建的查詢只是轉換爲SQL的一種方式,因此限制甚至可能不在Django上。 :-) –

+0

我聽到了。感謝您的洞察力。 – Furbeenator