創建對象(Creating objects)
創建 model 的新實例和實例化其他的 Python 類一樣:
- class Model( **kwargs)
其中的關鍵字參數就是你在你的 model 中定義的字段的名稱。要注意實例化一個 model 並不會操作數據庫,要保存到數據庫的話,你需要使用 save() 方法。
保存對象(Saving objects)
要將對象保存至數據庫,就調用 save():
- Model. save( [force_insert=False, force_update=False])
這個方法頗有些微妙之處,請查看下面幾節。
save() 方法的形式與之前的版本已經有所不同(加入了 force_insert 和 force_update 參數)。如果你要重寫該方法,就要留意這兩個參數。
自增主鍵(Auto-incrementing primary keys)
如果你的 model 有一個 AutoField 字段,它是一個自增主鍵,那么在你第一次調用 save() 方法時,就會計算得出這個自增主鍵的值,然后做為對象的一個屬性保存起來:
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.') >>> b2.id # Returns None, because b doesn't have an ID yet. >>> b2.save() >>> b2.id # Returns the ID of your new object.
在調用 save() 之前是拿不到 ID 值的,因為這個值是要靠數據庫來計算得出的,而非 Django。
(為使用方便,每個 model 默認都有一個 AutoField 自增字段,它的名稱是 id。不過你可以在某個字段上指定 primary_key=True,從而將這個字段變成主鍵字段。詳情請查看 AutoField。
pk 屬性 (The pk property)
- Model. pk
無論是你自己定義了主鍵,還是使用 Django 默認提供的主鍵,每個 model 都有一個指向主鍵的屬性,叫做 pk。它看上去只是是一個屬性,其實它是主鍵字段的一個別名。你可以象使用其他屬性一樣,獲取或是設置它的值,它會自動對主鍵字段的值進行更改。
顯式指定自增主鍵的值(Explicitly specifying auto-primary-key values)
如果一個 model 中含有一個 AutoField 自增字段,但是你在保存對象,並不想使用自動分配的 ID 值,而想顯示地定義新對象的 ID 值,那么只要在保存之時顯式的定義即可:
>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.') >>> b3.id # Returns 3. >>> b3.save() >>> b3.id # Returns 3.
如果你手動指定了自增主鍵的值,請事先確認主鍵的值並未在數據庫中出現過;否則,如果你創建新對象時,顯式指定的自增主鍵的值,卻已存在於數據庫中,那么 Django 就不會認為你是想創建對象,而是認為你想更改某個已存在的記錄。
以上面所給的 'Cheddar Talk' 博客為例,下面的代碼將覆蓋數據庫中原來的記錄:
b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.') b4.save() # Overrides the previous blog with ID=3!
要了解為什么會覆蓋掉記錄,請查看下面的 Django 如何判斷是更新還是創建(How Django knows to UPDATE vs. INSERT) 一節。
如果你能確實主鍵值不發生沖突,那么對於大量保存對象而言,指定自增主鍵的值是非常有用的。
在保存對象時都發生了什么(What happens when you save?)
在你保存對象時,Django 會執行下面的步驟:
-
發出預保存信號(Emit a pre-save signal)。 發出 django.db.models.signals.pre_save 信號(signal),然后監聽該信號的函式就會執行某些定制操作。
-
預處理數據(Pre-process the data)。 對角中的每個字段都根據字段所需的數據格式,對數據自動進行調整。
大多數字段是不需要預處理數據的,它們保持不變,只有那些有特殊行為的字段才需要預處理數據。例如,如果你的 model 中含有一個 DateField 字段,並且它指定了 auto_now=True,在預處理數據這個階段,就會修改該字段的數據,使其包含當前時間戳(我們的文檔還沒有將這些有“特殊行為”的字段完整地列出來)。
-
根據數據庫進行數據轉換(Prepare the data for the database)。 每個字段都要根據當前數據庫所要求的數據格式對當前值進行轉換。
大多數字段無須轉換,它們多是簡單的數據類型,比如整數,字符串,都是隨時可寫的 Python 對象。但是,有更多的復雜數據類型需要對數據進行轉換和調整。
例如,DateFields 字段使用的是一個 datetime 對象,這是一個 Python 類的實例,因為數據庫並不直接保存 datetime 對象,所以字段值必須被轉換成符合 ISO 標准的(ISO-compliant)的日期字符串,這樣數據庫才能識別。
-
將數據添加到數據庫中(Insert the data into the database)。 經過預處理和轉換的數據被組合成一條用以插入數據的 SQL 語句,從而完成對數據的添加。
-
發出已保存信號(Emit a post-save signal)。 發出 django.db.models.signals.post_save 信號,監聽該信號的函式們會執行某些既定的操作。
(Django 是如何判別更新還是創建的How Django knows to UPDATE vs. INSERT)
你可能已注意到,Django 的數據對象無論是創建對象還是保存修改過的對象,都是使用同樣的 save() 方法,因為 Django 已經對使用 INSERT 或 UPDATE SQL 語句的需求進行了抽象。因此,在你調用 save() 方法時,Django 會按照下列算法進行處理:
- 如果對象的主鍵屬性是被賦與一個真值(True)時,(例如,即不是 None,也不是空字符串),Django 會執行一個 SELECT 查詢以判定與主鍵相綁定的記錄是否存在。
- 如果該記錄存在,Django 就會執行另一個 UPDATE 查詢query。
- 如果未設置對象的主鍵屬性,或者如果雖然設置了主鍵,但是與其綁定的記錄卻不存在,那么 Django 不會執行一個 INSERT 操作。
有一點要注意的是:如果你不能保證主鍵值並被用過,那么在保存新對象時,要注意不要顯式指定主鍵的值。想在這個細節上有更多了解,請查看上面的 顯示指定自增變量的值(Explicitly specifying auto-primary-key values) 和下面的 強制添加或更新(Forcing an INSERT or UPDATE) 。
強制新增或更新(Forcing an INSERT or UPDATE)
在一些少見的場合中,運行 save() 方法時要求強制運行一條 SQL INSERT 語句,而非 UPDATE 語句。同理在某些情況下又強迫 UPDATE 而非 INSERT。在這種情況下,你可以通過設置 force_insert=True 或 force_update=True 參數的方式運行 save() 方法。同時提供兩個參數會拋開錯誤,因為 Django 不知道該 INSERT 還是 UPDATE。
需要使用這兩個參數的用例是很少見的。一般來說,Django 的默認操作大多是正確的,如果使用了這兩個參數,如果出現錯誤,會使得調試跟蹤變得困難,所以這個特性僅適用於高級應用。
根據已存在的字段更新屬性(Updating attributes based on existing fields)
有些時候,你需要對某個字段做簡單的運算,比如對當前值遞增或遞減,可以用下面的方法達到這個目的:
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese') >>> product.number_sold += 1 >>> product.save()
從數據庫中得到舊的 number_sold 值是 10,然后再將 11 寫回到數據庫中。
根據某個字段進行賦值,要好於直接地顯式賦值。Django 提供了 F()表達式(F() expressions) 來解決根據字段賦值的問題。使用 F() 可以將上面的例子改寫為:
>>> from django.db.models import F >>> product = Product.objects.get(name='Venezuelan Beaver Cheese') >>> product.number_sold = F('number_sold') + 1 >>> product.save()
這種方式並不是一開始就從數據庫中取出原始值,而是在執行 <>save()<> 操作的時候才根據取值字段從數據庫中取出原始值。
一旦對象被保存,你必須馬上重新得到對象,這樣才能得到你剛才得賦的字段值:
>>> product = Products.objects.get(pk=product.pk) >>> print product.number_sold 42
詳情請查看 use in update queries 文檔中 F() expressions這一節
對象刪除(Deleting objects)
- Model. delete()
-
對當前對象執行一條 DELETE SQL 語句。刪除操作僅僅作用於數據庫,Python 的實例對象依然存在,它所包含的字段同樣也存在。.
要了解更多細節,比如如何一次性刪除多個對象,請查看 對象刪除(Deleting objects).
其他的 model 實例方法(Other model instance methods)
有一部分方法有另外的特殊作用。
__str__
- Model. __str__()
__str__() 是一個 Python 式的 "魔術方法" ,它定義了在你調用了對象的 str() 方法時,返回值的內容。Django 在很多場合使用 str(obj) (或是相關的其他的函式,比如下面提到的 unicode(obj)),最常見的就是在 Django 管理后台中做為對象的顯示值;或是用來在模板中顯示對象。因此,你應該總是為 __str__ 返回一個友好且易讀的字符串。這樣做雖然不是必須的,不過我們仍建議您這樣做。不過在您到處狂寫 __str__ 之前,請繼續閱讀下面所講的 __unicode__ 方法。
例如:
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def __str__(self): # Note use of django.utils.encoding.smart_str() here because # first_name and last_name will be unicode strings. return smart_str('%s %s' % (self.first_name, self.last_name))
__unicode__
- Model. __unicode__()
在你使用對象的 unicode() 方法時,就會調用 __unicode__() 。因為你的 model 中的字段從數據庫中得到的是 Unicode 字符串,所以一般情況下你要為你的 model 寫一個 __unicode__() 方法。上面的例子可以簡單地改寫為:
class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name)
如果你在 model 上只定義了 __unicode__() 方法卻並沒有定義 __str__() 方法,Django 將自動提供一個會在內部調用 __unicode__() 的 __str__() 方法,並且將返回值自動轉換成 UTF-8 字符串。對此,在開發時有如下建議:只定義 __unicode__() 方法,讓 Django 在必要時自動做字符串轉換。
get_absolute_url
- Model. get_absolute_url()
定義 get_absolute_url() 方法會讓 Django 知道如何計算得到當前對象對應的網址 URL,例如:
def get_absolute_url(self): return "/people/%i/" % self.id
Django 在管理后台使用該方法。如果某個對象定義了 get_absolute_url() 方法,在它的對象修改頁就會出現一個 "View on site" 鏈接,它會彈出一個與當前對象相關的網頁,網站就是 get_absolute_url() 返回的 URL。
此外,Django 有些其他部分,也用到了 get_absolute_url() ,比如 RSS種子(syndication feed framework)。 get_absolute_url() 是一個對實際應用中很有用的快捷方法。
在模板中使用 get_absolute_url() 比直接在拼合網址要更好。例如,這種模板代碼就不是很好:
<a href="/people/{{ object.id }}/">{{ object.name }}</a>
但這種寫法就很好:
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
注意
get_absolute_url() 返回的 URL 只能包含 ASCII 字符串 (這是 URI 的規則所要求的,詳見 RFC 2396) ,所以在必要的時候,會進行重編碼 URL-encoded,而在代碼和模板中 get_absolute_url() 時,會直接輸出結果而不會進行網址重編碼。如果你有大量的 Unicode 網址要進行重編碼,可以使用 django.utils.encoding.iri_to_uri() 函式來處理。
permalink 裝飾器(The permalink decorator)
使用 get_absolute_url() 來獲取網址會帶來這樣一個問題:它違背了 DRY 原則: 對象的 URL 定義同時出現在 URLConf 配置文件(urls.py)和 model 定義中。
你可以使用 permalink 裝飾器將 model 與 URLConf 相剝離:
- permalink()
該裝飾器需要一個視圖函式,一個位置參數的列表以及一個命名參數的字典(這個是可選的),然后 Django 就會根據 URLconf ,填入你傳遞的參數,從而得到完整的網址 URL。例如,你的 URLconf 有這么一行:
(r'^people/(\d+)/$', 'people.views.details'),
...你 model 中的 get_absolute_url 方法如下:
from django.db import models @models.permalink def get_absolute_url(self): return ('people.views.details', [str(self.id)])
再來一個例子,如果你的 URLconf 有這么一行:
(r'/archive/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', archive_view)
...然后 permalink()如下:
@models.permalink def get_absolute_url(self): return ('archive_view', (), { 'year': self.created.year, 'month': self.created.month, 'day': self.created.day})
注意上例中的第二個參數是一個空隊列,因為我們只要傳遞關鍵字參數就行了,用不到位置參數。
用這種方法,你就可以將對象的絕對網址與用到它的視圖聯系起來,就不用再重復定義 URL 信息了,仍然可以象往常一樣在模板中 get_absolute_url 方法。
在某些情況下,比如使用通用視圖或是對多個 model 重用自定義視圖,指定視圖函式可能就會與反向 URL 匹配相沖突(因為多個匹配模式同時指向同一個視圖)。
為了解決這個問題,Django 提供了命名URL匹配模式(named URL patterns)。使用命名 URL 匹配時,我們可以給匹配模式命名,然后可以直接引用這個名稱而不用使用視圖函式。只要將模式元組變成一個 url 函式就能完成對命名匹配模式的定義:
from django.conf.urls.defaults import * url(r'^people/(\d+)/$', 'django.views.generic.list_detail.object_detail', name='people_view'),
...接下來用模式的命名代替視圖函式:
from django.db.models import permalink def get_absolute_url(self): return ('people_view', [str(self.id)]) get_absolute_url = permalink(get_absolute_url)
要了解命名 URL 匹配模式,請查看 URL 部分(URL dispatch documentation).
其他的實例方法(Extra instance methods)
除了 save(), delete() 之外,一個 model 對象可能還有下列方法:
- Model. get_FOO_display()
如果 model 有字段設置了 choices ,對象就會提供 get_FOO_display() 方法,這里的 FOO 就是字段的名稱。這個方法返回給用戶看的那個可讀性好的值,例如:
GENDER_CHOICES = ( ('M', 'Male'), ('F', 'Female'), ) class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
...每個 Person 實例都有一個 get_gender_display() 方法。例如:
>>> p = Person(name='John', gender='M') >>> p.save() >>> p.gender 'M' >>> p.get_gender_display() 'Male'
- Model. get_next_by_FOO( **kwargs)
- Model. get_previous_by_FOO( **kwargs)
如果 model 中的 DateField 字段和 DateTimeField 字段沒有設置 null=True,那么對象就會提供 get_next_by_FOO() 方法和 get_previous_by_FOO() 方法。這里的 FOO 就是時間字段的名稱。它根據當時時間字段的值返回下一個或是前一個對象。如果沒有找到就會拋開 DoesNotExist 異常。
這兩個方法的參數都是可選的,這些參數在 字段篩選條件(Field lookups) 中有詳細介紹。
要注意:如果在使用這兩個方法時遇到了相同的時間,那么它們就會將 ID 值做為第二條件,這樣才能保證記錄沒有重復或遺漏。