標籤:style blog http color 使用 io 資料 for
上一篇寫到MakingQuey中的filter,本篇接著來。
10)-擴充多值的關係
如果對一個ManyToManyField或ForeignKey的表進行filter過濾查詢的話,有2中方法可以用。分別是:
#1Blog.objects.filter(entry__headline__contains=‘Lennon‘, entry__pub_date__year=2008)#2Blog.objects.filter(entry__headline__contains=‘Lennon‘).filter( entry__pub_date__year=2008)
假設,現有一個blog表,它對應了很多的entry的表,而且這個blog既有含有“Lennon”的entry,也有含有2008的entry,但是沒有那種2者都有的entry,那麼第一個方法過濾的是2者都有的entry,所以沒有合格blog返回。第一種方法可以理解為“並且”的意思。第二種是第一個filter返回含有“Lennon”的blog,然後在這個blog集合中,再選有2008的,所以會有blog返回。開始理解起來很費勁,抓住特點:一個blog對應多個entry。例如,blog1對應了entry1,entry2,entry3,其中entry1隻有“Lennon”,沒有2008;entry2隻有2008,沒有“Lennon”;entry3則什麼都沒有。那麼用第一種方法來filter的時候,則沒有滿足既有Lennon又有2008的entry;第二種方法的第一個filter,因為entry1有Lennon,所以blog1為傳回值,然後對傳回值blog1進行filter,條件為有2008,而blog1中的entry2是有2008的,所以blog1作為傳回值。
這個也適用於exlude().
11)-filters可以引用model中的fields
class F
目前為止的例子中,都是用field中的值來作為filter的條件來進行資料查詢。但是如果需要把一個field的值與他所在的model的其他field的值呢?
django提供F()運算式來實現這種比較。F()的執行個體是在一個query中來對model中的field進行引用。這些引用可以作為filter的條件來進行過濾。
例如,要查詢blog的entry,comments滿足的數量比pingbacks的多,可以用F()來作用在pingbacks上,然後把結果作為filter的條件:
>>> from django.db.models import F>>> Entry.objects.filter(n_comments__gt=F(‘n_pingbacks‘))
注意:F()運算式中是n_comments比大n_pingbacks。所以,也有倍數、加、減這樣的操作可以來比較:
>>> Entry.objects.filter(n_comments__gt=F(‘n_pingbacks‘) * 2)#
n_comments比n_pingbacks的2倍多
>>> Entry.objects.filter(rating__lt=F(‘n_comments‘) + F(‘n_pingbacks‘))#n_comments與n_pingbacks的和比rating多
gt是多理解為>,lt理解為<.
而下面的例子則是兩個字串一樣:
>>> Entry.objects.filter(authors__name=F(‘blog__name‘))
12)-pk
是primary key的意思:
>>> Blog.objects.get(id__exact=14) # Explicit form>>> Blog.objects.get(id=14) # __exact is implied>>> Blog.objects.get(pk=14) # pk implies id__exact
# Get blogs entries with id 1, 4 and 7>>> Blog.objects.filter(pk__in=[1,4,7])# Get all blog entries with id > 14>>> Blog.objects.filter(pk__gt=14)
同樣應該有這些符號的組合,分別是__雙底線,lt,gt,exact等等。
如果查詢含有%符號的,下例給出:
>>> Entry.objects.filter(headline__contains=‘%‘)
同樣的_底線符號也是,django會自動來處理的,就像上邊這行程式這樣寫就行了。
5-QuerySet的緩衝
每個QuerySet都有緩衝來降低對資料庫database的訪問。弄明白了之後,可以寫出高效率的代碼。
在新建立的QuerySet中,緩衝是空的。當QuerySet第一次 被計算的時候,django把QuerySet放在其緩衝中,然後把具體要查詢的資料項目作為結果返回。把QuerySet這種緩衝的行為時刻記住,有的時候很容易使用不當的。例如下面,建立了2個QuerySet,分別使用後,就丟掉了,這樣造成了2次訪問database,而且第二個e很可能不是第一個e,因為在第二個發生之前,database很可能已經被寫入新資料或刪除什麼東西了:
>>> print([e.headline for e in Entry.objects.all()])>>> print([e.pub_date for e in Entry.objects.all()])
正確的做法如下,建立個變數來存放QuerySet,讓它一直放在記憶體中,然後每次用的時候從記憶體中去讀取:
>>> queryset = Entry.objects.all()>>> print([p.headline for p in queryset]) # Evaluate the query set.>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
注意,第一行並沒有touch the database,訪問資料庫發生在第二行代碼。
QuerySet也並不是總緩衝結果的,當計算queryset的一部分的時候,要緩衝,但是如果只是用一個下一層的queryset來訪問結果的時候,並沒有緩衝。這也意味著,可以用數組分區或者一個索引來限制queryset而不產生緩衝,例如下面的例子,顯示只定義了一個queryset來存放objects,然後用數組來訪問其中的某一個,這樣就沒有緩衝,每次都要重新訪問資料庫:
>>> queryset = Entry.objects.all()>>> print queryset[5] # Queries the database>>> print queryset[5] # Queries the database again
然而,如果整個queryset都被計算了,那麼就會產生緩衝:
>>> queryset = Entry.objects.all()>>> [entry for entry in queryset] # Queries the database>>> print queryset[5] # Uses cache>>> print queryset[5] # Uses cache
還有一些例子也是整個querset都被計算了,當然產生了緩衝:
>>> [entry for entry in queryset]>>> bool(queryset)>>> entry in queryset>>> list(queryset)
6-用Q對象來進行複雜的查詢
在filter()方法中,用關鍵字來查詢,這些關鍵字是AND的關係,即”並且“,如果需要進行更複雜的查詢,例如OR關係,可以用Q對象這種方法。
Q對象(django.db.models.Q)是用來封裝關鍵字的集合的。這些關鍵字包括上文的lookup查詢中所提到的關鍵字。例如,下面這個Q對象封裝了LIKE查詢:
from django.db.models import QQ(question__startswith=‘What‘)
Q對象可以用邏輯符號&或者|來串連,結果返回一個Q對象:
Q(question__startswith=‘Who‘) | Q(question__startswith=‘What‘)
同等於SQL語句:
WHERE question LIKE ‘Who%‘ OR question LIKE ‘What%‘
可以把Q對象組合成複雜的表達,也可以用~符號,代表”NOT,非“的意思:
Q(question__startswith=‘Who‘) | ~Q(pub_date__year=2005)
每個查詢函數(filter(), get(), exclude())都需要有關鍵字,Q對象就可以作為這些查詢函數的關鍵字:
Poll.objects.get( Q(question__startswith=‘Who‘), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
翻譯成SQL語句就相當痛苦了:
SELECT * from polls WHERE question LIKE ‘Who%‘ AND (pub_date = ‘2005-05-02‘ OR pub_date = ‘2005-05-06‘)
查詢函數也可以混合使用keywords和Q object,但是要注意,Q object必須在keyword之前定義:
#正確的Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith=‘Who‘)
#錯誤的Poll.objects.get( question__startswith=‘Who‘, Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
7-比較對象
用python標準的比較操作符==就可以進行model執行個體的比較。而這實際上實在比較primarykey的值:
>>> some_entry == other_entry>>> some_entry.id == other_entry.id#實際上
如果model的primarykey不叫id,沒關係,照樣還是在比較primarykey,例如一個model的primarykey是name,則下面2行代碼是同樣意思的:
>>> some_obj == other_obj>>> some_obj.name == other_obj.name
8-刪除對象
delete()用來進行對象的刪除,而且是立即生效,並且沒有傳回值:
e.delete()
也可以批量來進行刪除操作,每個QuerySet都有delete()操作,可以把QuerySet中的所有對象都刪掉:
Entry.objects.filter(pub_date__year=2005).delete()
delete是Manager的一個方法,但是由於怕使用者誤操作而沒有顯式的顯現出來。因為delete是純粹的在SQL中執行的方法,所以不需要使用者在過程中去認為的調用。如果你在一個model的class定義中自己定義了一個delete()的話,那麼就相當於覆蓋掉了Manager提供的delete,這樣就需要使用者自己去調用了,而且這種自己定義的delete()還不能批量的來操作,也就是不能作為QuerySet中的參數來調用,而是需要自己對每個object單獨進行delete一次。
django提供的delete()方法,會刪除由foreignkey來關聯到一起的object:
b = Blog.objects.get(pk=1)# This will delete the Blog and all of its Entry objects.b.delete()
這種串聯的用法是通過 on_delete 參數傳遞給Foreignkey的。
delete是Manager的一個方法,為了防止使用者誤操作,所以不用顯式調用,但是如果確實需要調用delete()來刪除所有的objects的話,應該明確的指出來這個QuerySet:
Entry.objects.all().delete()
9-複製model執行個體
儘管沒有built-in的方法來實現model 執行個體的複製,但是可以用model的所有的fields的值來建立一個新的執行個體,最簡單的情況就是,先把pk設定成None,然後再save:
1 blog = Blog(name=‘My blog‘, tagline=‘Blogging is easy‘)2 blog.save() # blog.pk == 13 4 blog.pk = None5 blog.save() # blog.pk == 2
思考:也就是,第1行代碼只是把Blog內容放在了blog這個變數中,並沒寫進去資料庫,只有調用save()方法時,才發生寫的操作。而且每當調用save(),都相當於要把這個資料(blog)寫進資料庫。而資料庫的pk是唯一而不能重複的,所有先要把blog.pk設成None,然後再操作,由SQL語句把pk由依次遞增為2了。
如果有model的繼承時,會略複雜一些:
class ThemeBlog(Blog): theme = models.CharField(max_length=200)django_blog = ThemeBlog(name=‘Django‘, tagline=‘Django is easy‘, theme=‘python‘)django_blog.save() # django_blog.pk == 3
因為繼承的工作原理,這次需要把pk和id都設成None了:
django_blog.pk = Nonedjango_blog.id = Nonedjango_blog.save() # django_blog.pk == 4
這個過程並沒有複製相關聯的objects,如果想要複製這種關係,需要多寫一些代碼了。在本文的例子中,Entry和Author是多對多的關係(ManyToManyField):
1 entry = Entry.objects.all()[0] # some previous entry2 old_authors = entry.authors.all()3 entry.pk = None4 entry.save()5 entry.authors = old_authors # saves new many2many relations
今天到這,明天繼續!