建立完Model以後, Django 自動爲你提供一套數據庫抽象層的API,利用它能夠完成建立,提取,更新,刪除對象的操做。html
如下面的Model爲例:python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class
Blog(models.Model):
name
=
models.CharField(max_length
=
100
)
tagline
=
models.TextField()
# On Python 3: def __str__(self):
def
__unicode__(
self
):
return
self
.name
class
Author(models.Model):
name
=
models.CharField(max_length
=
50
)
email
=
models.EmailField()
# On Python 3: def __str__(self):
def
__unicode__(
self
):
return
self
.name
class
Entry(models.Model):
blog
=
models.ForeignKey(Blog)
headline
=
models.CharField(max_length
=
255
)
body_text
=
models.TextField()
pub_date
=
models.DateField()
mod_date
=
models.DateField()
authors
=
models.ManyToManyField(Author)
n_comments
=
models.IntegerField()
n_pingbacks
=
models.IntegerField()
rating
=
models.IntegerField()
# On Python 3: def __str__(self):
def
__unicode__(
self
):
return
self
.headline
|
1
2
|
>>> b
=
Blog(name
=
'Beatles Blog'
, tagline
=
'All the latest Beatles news.'
)
>>> b.save()
|
Django 用一種很直觀的表達方式將Python對象和數據表對應起來:一個model類對應一張數據表,一個model實例對應表中的某一行記錄。git
以建立對象爲例:只要將關鍵字參數傳遞給model類,而後調用save()保存到數據庫便可github
這段代碼就會在幕後執行一條INSERT SQL語句。除非你顯式地調用save()方法,不然Django不會保存到數據庫中。數據庫
你可使用create()方法一次完成新建並保存對象的操做。django
1
2
|
>> b5.name
=
'New name'
>> b5.save()
|
Saving ForeignKey and ManyToManyField fields編程
更新ForeignKey字段和保存普通字段沒什麼差異;只是在給字段分配對象時要注意對象類型必定要正確:api
1
2
3
4
|
>>> entry
=
Entry.objects.get(pk
=
1
)
>>> cheese_blog
=
Blog.objects.get(name
=
"Cheddar Talk"
)
>>> entry.blog
=
cheese_blog
>>> entry.save()
|
更新ManyToManyField就有些不一樣;要在字段上使用add()方法來添加關係:數組
1
2
|
>>> joe
=
Author.objects.create(name
=
"Joe"
)
>>> entry.authors.add(joe)
|
一次添加多個:緩存
1
2
3
4
5
|
>>> john
=
Author.objects.create(name
=
"John"
)
>>> paul
=
Author.objects.create(name
=
"Paul"
)
>>> george
=
Author.objects.create(name
=
"George"
)
>>> ringo
=
Author.objects.create(name
=
"Ringo"
)
>>> entry.authors.add(john, paul, george, ringo)
|
要檢索數據庫中的對象,就要爲你的model 類構造一個查詢集QuerySet。一個QuerySet就表明數據庫中的一組數據。它能夠有一個或不少個,也能夠經過filter根據給定的參數對數據集作進一步篩選。在SQL術語中,QuerySet至關於SELECT語句,filter至關於WHERE或LIMIT這樣的限定從句。
檢索全部的對象
1
|
all_entries
=
Entry.objects.
all
()
|
使用過濾器filter檢索特定的對象
filter(**kwargs)
返回知足篩選條件的新QuerySet
exclude(**kwargs)
返回不知足篩選條件的新QuerySet
1
|
Entry.objects.
filter
(pub_date__year
=
2006
)
|
鏈式過濾:
1
2
3
4
5
6
7
|
>>> Entry.objects.
filter
(
... headline__startswith
=
'What'
... ).exclude(
... pub_date__gte
=
datetime.date.today()
... ).
filter
(
... pub_date__gte
=
datetime(
2005
,
1
,
30
)
... )
|
查詢一個單獨的對象
1
|
>>> one_entry
=
Entry.objects.get(pk
=
1
)
|
注意當使用get查詢時若對象不存在會產生DoesNotExist異常。
QuerySet是惰性的
QuerySets是惰性的。建立 QuerySet 的動做不涉及任何數據庫操做。你能夠一直添加過濾器,在這個過程當中,Django不會執行任何數據庫查詢,除非QuerySet被執行。
切片
能夠用python的數組切片語法來限制你的QuerySet以獲得一部分結果。它等價於SQL中的LIMIT和OFFSET。
例如,下面的這個例子返回前五個對象:
1
|
>>> Entry.objects.
all
()[:
5
]
|
Django 不支持對查詢集作負數索引。
跨關係查詢
Django 提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用雙下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的 model 爲止。
1
|
Blog.objects.
filter
(entry__authors__name
=
'Lennon'
)
|
要注意的是若是跨關係對象有多個符合條件,被查詢對象會返回屢次:
1
|
Blog.objects.
filter
(entry__headline
=
'note'
)
|
上面若是有個Blog連接多個headline爲note的Entry對象,此Blog會返回屢次。
跨一對多/多對多關係
對於包含在同一個filter()中的篩選條件,查詢集要同時知足全部篩選條件。而對於連續的filter(),查詢集的範圍是依次限定的。但對於跨一對多/多對多關係查詢來講,在第二種狀況下,篩選條件針對的是主model全部的關聯對象,而不是被前面的filter()過濾後的關聯對象。
這聽起來會讓人迷糊,舉個例子會講得更清楚。要檢索這樣的 blog:它要關係一個大標題中含有 "Lennon" 而且在2008年出版的entry(這個entry同時知足這兩個條件),能夠這樣寫:
1
2
|
Blog.objects.
filter
(entry__headline__contains
=
'Lennon'
,
entry__pub_date__year
=
2008
)
|
要檢索另一種 blog:它關聯一個大標題含有"Lennon"的 entry ,又關聯一個在2008年出版的 entry (一個entry的大標題含有Lennon,同一個或另外一個entry是在2008年出版的)。能夠這樣寫:
1
2
|
Blog.objects.
filter
(entry__headline__contains
=
'Lennon'
).
filter
(
entry__pub_date__year
=
2008
)
|
上述原則一樣適用於exclude():一個單獨exclude()中的全部篩選條件都是做用於同一個實例 (若是這些條件都是針對同一個一對多/多對多的關係)。連續的filter()或exclude()卻根據一樣的篩選條件,做用於不一樣的關聯對象。
Filters can reference fields on the model
class F
在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?
Django 提供F()來作這樣的比較。F()的實例能夠在查詢中引用字段,來比較同一個model實例中兩個不一樣字段的值。
例如:要查詢回覆數(comments)大於廣播數(pingbacks)的博文(blog entries),能夠構造一個 F() 對象在查詢中引用評論數量:
1
2
|
>>>
from
django.db.models
import
F
>>> Entry.objects.
filter
(n_comments__gt
=
F(
'n_pingbacks'
))
|
Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做。例如,要找到廣播數等於評論數兩倍的博文,能夠這樣修改查詢語句:
1
|
>>> Entry.objects.
filter
(n_comments__gt
=
F(
'n_pingbacks'
)
*
2
)
|
要查找閱讀數量小於評論數與廣播數之和的博文,查詢以下:
1
|
>>> Entry.objects.
filter
(rating__lt
=
F(
'n_comments'
)
+
F(
'n_pingbacks'
))
|
你也能夠在 F() 對象中使用雙下劃線作跨關係查詢。F() 對象使用雙下劃線引入必要的關聯對象。例如,要查詢博客(blog)名稱與做者(author)名稱相同的博文(entry),查詢就能夠這樣寫:
1
|
>>> Entry.objects.
filter
(authors__name
=
F(
'blog__name'
))
|
對於Date和datetime域,能夠加減timedelta對象。
返回出版3天之後修改的博文:
1
2
|
>>>
from
datetime
import
timedelta
>>> Entry.objects.
filter
(mod_date__gt
=
F(
'pub_date'
)
+
timedelta(days
=
3
))
|
F對象如今也支持位運算(bitand(),bitor()):
1
|
>>> F(
'somefield'
).bitand(
16
)
|
主鍵查詢的簡捷方式 (The pk lookup shortcut)
爲使用方便考慮,Django 用pk表明主鍵"primary key"。
以Blog爲例, 主鍵是id字段,因此下面三個語句都是等價的:
1
2
3
|
>>> 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
|
在LIKE語句中轉義百分號%和下劃線_ (Escaping percent signs and underscores in LIKE statements)
字段篩選條件至關於LIKE SQL語句 (iexact, contains, icontains, startswith, istartswith, endswith 和 iendswith) ,它會自動轉義兩個特殊符號,百分號(%)和下劃線(_)。(在LIKE語句中,百分號%表示多字符匹配,而下劃線_表示單字符匹配。)
這就意味着咱們能夠直接使用這兩個字符,而不用考慮他們的SQL語義。例如,要查詢大標題中含有一個百分號%的entry:
1
|
>>> Entry.objects.
filter
(headline__contains
=
'%'
)
|
Django會處理轉義,下劃線_和百分號%的處理方式相同,Django都會自動轉義。
緩存和查詢
每一個QuerySet都包含一個緩存,以減小對數據庫的訪問。要編寫高效代碼,就要理解緩存是如何工做的。
一個QuerySet時剛剛建立的時候,緩存是空的。 QuerySet 第一次運行時,會執行數據庫查詢,接下Django就在QuerySet的緩存中保存查詢的結果,並根據請求返回這些結果(好比,後面再次調用這個 QuerySet 的時候)。再次運行 QuerySet 時就會重用這些緩存結果。
要牢住上面所說的緩存行爲,不然在使用 QuerySet 時可能會給你形成不小的麻煩。例如,建立下面兩個 QuerySet ,並對它們求值,而後釋放:
1
2
|
>>>
print
([e.headline
for
e
in
Entry.objects.
all
()])
>>>
print
([e.pub_date
for
e
in
Entry.objects.
all
()])
|
這就意味着相同的數據庫查詢將執行兩次,事實上讀取了兩次數據庫。並且,這兩次讀出來的列表可能並不徹底相同,由於存在這種可能:在兩次讀取之間,某個 Entry 被添加到數據庫中,或是被刪除了。
要避免這個問題,只要簡單地保存QuerySet而後重用便可:
1
2
3
|
>>> queryset
=
Entry.objects.
all
()
>>>
print
([p.headline
for
p
in
queryset])
# 對查詢集求值.
>>>
print
([p.pub_date
for
p
in
queryset])
# 使用緩存.
|
When querysets are not cached
查詢集並不老是緩存結果,當查詢集執行部分查詢時,會先檢查緩存,若是它沒有被填充,部分查詢返回的結果不會被緩存。這意味着,使用切片查詢不會填充緩存。
例如,重複的切片查詢每次都會訪問數據庫:
1
2
3
|
>>> queryset
=
Entry.objects.
all
()
>>>
print
queryset[
5
]
# 訪問數據庫
>>>
print
queryset[
5
]
# 再次訪問數據庫
|
可是,若是整個查詢集已經被求值,切片查詢會使用緩存:
1
2
3
4
|
>>> queryset
=
Entry.objects.
all
()
>>> [entry
for
entry
in
queryset]
# 查詢數據庫
>>>
print
queryset[
5
]
# 使用緩存
>>>
print
queryset[
5
]
# 使用緩存
|
下面是一些會填充緩存的操做:
1
2
3
4
|
>>> [entry
for
entry
in
queryset]
>>>
bool
(queryset)
>>> entry
in
queryset
>>>
list
(queryset)
|
在filter() 等函式中關鍵字參數彼此之間都是 "AND" 關係。若是你要執行更復雜的查詢(好比,實現篩選條件的OR關係),可使用Q對象。
Q對象(django.db.models.Q)是用來封裝一組查詢關鍵字的對象。
例如,下面這個Q對象封裝了一個單獨的 LIKE 查詢:
1
2
|
from
django.db.models
import
Q
Q(question__startswith
=
'What'
)
|
Q 對象能夠用 & 和 | 運算符進行鏈接。當某個操做鏈接兩個 Q 對象時,就會產生一個新的等價的 Q 對象。
例如,下面這段語句就產生了一個Q,這是用 "OR" 關係鏈接的兩個"question__startswith" 查詢:
1
|
Q(question__startswith
=
'Who'
) | Q(question__startswith
=
'What'
)
|
上面的例子等價於下面的 SQL WHERE 從句:
1
|
WHERE question LIKE
'Who%'
OR question LIKE
'What%'
|
你能夠用 & 和 | 鏈接任意多的Q對象,並且能夠用括號分組。Q 對象也能夠用 ~ 操做取反,並且普通查詢和取反查詢(NOT)能夠鏈接在一塊兒使用:
1
|
Q(question__startswith
=
'Who'
) | ~Q(pub_date__year
=
2005
)
|
每種查詢函式(好比 filter(), exclude(), get()) 除了能接收關鍵字參數之外,也能以位置參數的形式接受一個或多個 Q 對象。若是你給查詢函式傳遞了多個 Q 對象,那麼它們彼此間都是 "AND" 關係。例如:
1
2
3
4
|
Poll.objects.get(
Q(question__startswith
=
'Who'
),
Q(pub_date
=
date(
2005
,
5
,
2
)) | Q(pub_date
=
date(
2005
,
5
,
6
))
)
|
... 大致能夠翻譯爲下面的 SQL:
1
2
|
SELECT
*
from
polls WHERE question LIKE
'Who%'
AND (pub_date
=
'2005-05-02'
OR pub_date
=
'2005-05-06'
)
|
查找函式能夠混用 Q 對象和關鍵字參數。查詢函式的全部參數(Q 關係和關鍵字參數) 都是 "AND" 關係。可是,若是參數中有 Q 對象,它必須排在全部的關鍵字參數以前。例如:
1
2
3
|
Poll.objects.get(
Q(pub_date
=
date(
2005
,
5
,
2
)) | Q(pub_date
=
date(
2005
,
5
,
6
)),
question__startswith
=
'Who'
)
|
是一個有效的查詢。但下面這個查詢雖然看上去和前者等價:
1
2
3
4
|
# 無效查詢
Poll.objects.get(
question__startswith
=
'Who'
,
Q(pub_date
=
date(
2005
,
5
,
2
)) | Q(pub_date
=
date(
2005
,
5
,
6
)))
|
但這個查詢倒是無效的。
要比較兩個對象,就和Python 同樣,使用雙等號運算符:==。實際上比較的是兩個model的主鍵值。
以上面的Entry爲例,下面兩個語句是等價的:
1
2
|
>>> some_entry
=
=
other_entry
>>> some_entry.
id
=
=
other_entry.
id
|
若是model的主鍵名稱不是id,也不要緊。Django會自動比較主鍵的值,而無論他們的名稱是什麼。例如,若是一個model的主鍵字段名稱是name,那麼下面兩個語句是等價的:
1
2
|
>>> some_obj
=
=
other_obj
>>> some_obj.name
=
=
other_obj.name
|
刪除方法就是delete()。它運行時當即刪除對象而不返回任何值。例如:
1
|
e.delete()
|
你也能夠一次性刪除多個對象。每一個QuerySet 都有一個delete() 方法,它一次性刪除 QuerySet 中全部的對象。
例如,下面的代碼將刪除 pub_date 是2005年的 Entry 對象:
1
|
Entry.objects.
filter
(pub_date__year
=
2005
).delete()
|
要牢記這一點:不管在什麼狀況下,QuerySet 中的 delete() 方法都只使用一條 SQL 語句一次性刪除全部對象,而並非分別刪除每一個對象。若是你想使用在model中自定義的delete() 方法,就要自行調用每一個對象的delete方法。(例如,遍歷 QuerySet,在每一個對象上調用 delete()方法),而不是使用QuerySe 中的 delete()方法。
在Django刪除對象時,會模仿SQL約束ON DELETE CASCADE的行爲,換句話說,刪除一個對象時也會刪除與它相關聯的外鍵對象。例如:
1
2
3
|
b
=
Blog.objects.get(pk
=
1
)
# This will delete the Blog and all of its Entry objects.
b.delete()
|
要注意的是: delete() 方法是QuerySet上的方法,但並不適用於 Manager 自己。這是一種保護機制,是爲了不意外地調用Entry.objects.delete() 方法致使 全部的 記錄被誤刪除。若是你確認要刪除全部的對象,那麼你必須顯式地調用:
1
|
Entry.objects.
all
().delete()
|
複製對象並無內置的函數,最簡單的狀況,將pk設爲None。
1
2
3
4
5
|
blog
=
Blog(name
=
'My blog'
, tagline
=
'Blogging is easy'
)
blog.save()
# blog.pk == 1
blog.pk
=
None
blog.save()
# blog.pk == 2
|
若是使用繼承的話,狀況會複雜一點:
1
2
3
4
5
|
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:
1
2
3
|
django_blog.pk
=
None
django_blog.
id
=
None
django_blog.save()
# django_blog.pk == 4
|
這樣寫是不會複製關係對象的,要複製關係對象,還須要一點代碼:
1
2
3
4
5
|
entry
=
Entry.objects.
all
()[
0
]
# some previous entry
old_authors
=
entry.authors.
all
()
entry.pk
=
None
entry.save()
entry.authors
=
old_authors
# saves new many2many relations
|
有時你想對QuerySet中的全部對象,一次更新某個字段的值。這個要求能夠用 update() 方法完成。例如:
1
2
|