該文檔詳細(xì)描述模型 的API。它建立在模型 和執(zhí)行查詢 的資料之上, 所以在閱讀這篇文檔之前,你可能會(huì)想要先閱讀并理解那兩篇文檔。
我們將用執(zhí)行查詢中所展現(xiàn)的 博客應(yīng)用模型 來(lái)貫穿這篇參考文獻(xiàn)。
要?jiǎng)?chuàng)建模型的一個(gè)新實(shí)例,只需要像其它Python 類一樣實(shí)例化它:
class Model(**kwargs)
關(guān)鍵字參數(shù)就是在你的模型中定義的字段的名字。注意,實(shí)例化一個(gè)模型不會(huì)訪問(wèn)數(shù)據(jù)庫(kù);若要保存,你需要save() 一下。
注
也許你會(huì)想通過(guò)重寫
__init__方法來(lái)自定義模型。無(wú)論如何,如果你這么做了,小心不要改變了調(diào)用簽名——任何改變都可能阻礙模型實(shí)例被保存。嘗試使用下面這些方法之一,而不是重寫init:1. 在模型類中增加一個(gè)類方法:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
@classmethod
def create(cls, title):
book = cls(title=title)
# do something with the book
return book
book = Book.create("Pride and Prejudice")
2. 在自定義管理器中添加一個(gè)方法(推薦):
class BookManager(models.Manager):
def create_book(self, title):
book = self.create(title=title)
# do something with the book
return book
class Book(models.Model):
title = models.CharField(max_length=100)
objects = BookManager()
book = Book.objects.create_book("Pride and Prejudice")
classmethod Model.from_db(db, field_names, values)
New in Django 1.8.
from_db() 方法用于自定義從數(shù)據(jù)庫(kù)加載時(shí)模型實(shí)例的創(chuàng)建。
db 參數(shù)包含數(shù)據(jù)庫(kù)的別名,field_names 包含所有加載的字段的名稱,values 包含field_names 中每個(gè)字段加載的值。field_names 與values 的順序相同,所以可以使用cls(**(zip(field_names, values))) 來(lái)實(shí)例化對(duì)象。如果模型的所有字段都提供,會(huì)保證values 的順序與__init__() 所期望的一致。這表示此時(shí)實(shí)例可以通過(guò)cls(*values) 創(chuàng)建??梢酝ㄟ^(guò)cls._deferred來(lái)檢查是否提供所有的字段 —— 如果為 False,那么所有的字段都已經(jīng)從數(shù)據(jù)庫(kù)中加載。
除了創(chuàng)建新模型之前,from_db() 必須設(shè)置新實(shí)例_state 屬性中的adding 和 db 標(biāo)志位。
下面的示例演示如何保存從數(shù)據(jù)庫(kù)中加載進(jìn)來(lái)的字段原始值:
@classmethod
def from_db(cls, db, field_names, values):
# default implementation of from_db() (could be replaced
# with super())
if cls._deferred:
instance = cls(**zip(field_names, values))
else:
instance = cls(*values)
instance._state.adding = False
instance._state.db = db
# customization to store the original field values on the instance
instance._loaded_values = zip(field_names, values)
return instance
def save(self, *args, **kwargs):
# Check how the current values differ from ._loaded_values. For example,
# prevent changing the creator_id of the model. (This example doesn't
# support cases where 'creator_id' is deferred).
if not self._state.adding and (
self.creator_id != self._loaded_values['creator_id']):
raise ValueError("Updating the value of creator isn't allowed")
super(...).save(*args, **kwargs)
上面的示例演示from_db()的完整實(shí)現(xiàn)。當(dāng)然在這里的from_db()中完全可以只用super() 調(diào)用。
Model.refresh_from_db(using=None, fields=None, **kwargs)
New in Django 1.8.
如果你需要從數(shù)據(jù)庫(kù)重新加載模型的一個(gè)值,你可以使用 refresh_from_db() 方法。當(dāng)不帶參數(shù)調(diào)用這個(gè)方法時(shí),將完成以下的動(dòng)作:
模型的所有非延遲字段都更新成數(shù)據(jù)庫(kù)中的當(dāng)前值。
之前加載的關(guān)聯(lián)實(shí)例,如果關(guān)聯(lián)的值不再合法,將從重新加載的實(shí)例中刪除。例如,如果重新加載的實(shí)例有一個(gè)外鍵到另外一個(gè)模型Author,那么如果 obj.author_id != obj.author.id,obj.author 將被扔掉并在下次訪問(wèn)它時(shí)根據(jù)obj.author_id 的值重新加載。
注意,只有本模型的字段會(huì)從數(shù)據(jù)庫(kù)重新加載。其它依賴數(shù)據(jù)庫(kù)的值不會(huì)重新加載,例如聚合的結(jié)果。
重新加載使用的數(shù)據(jù)庫(kù)與實(shí)例加載時(shí)使用的數(shù)據(jù)庫(kù)相同,如果實(shí)例不是從數(shù)據(jù)庫(kù)加載的則使用默認(rèn)的數(shù)據(jù)庫(kù)??梢允褂?code>using 參數(shù)來(lái)強(qiáng)制指定重新加載的數(shù)據(jù)庫(kù)。
可以回使用fields 參數(shù)強(qiáng)制設(shè)置加載的字段。
例如,要測(cè)試update() 調(diào)用是否得到預(yù)期的更新,可以編寫類似下面的測(cè)試:
def test_update_result(self):
obj = MyModel.objects.create(val=1)
MyModel.objects.filter(pk=obj.pk).update(val=F('val') + 1)
# At this point obj.val is still 1, but the value in the database
# was updated to 2. The object's updated value needs to be reloaded
# from the database.
obj.refresh_from_db()
self.assertEqual(obj.val, 2)
注意,當(dāng)訪問(wèn)延遲的字段時(shí),延遲字段的加載會(huì)通過(guò)這個(gè)方法加載。所以可以自定義延遲加載的行為。下面的實(shí)例演示如何在重新加載一個(gè)延遲字段時(shí)重新加載所有的實(shí)例字段:
class ExampleModel(models.Model):
def refresh_from_db(self, using=None, fields=None, **kwargs):
# fields contains the name of the deferred field to be
# loaded.
if fields is not None:
fields = set(fields)
deferred_fields = self.get_deferred_fields()
# If any deferred field is going to be loaded
if fields.intersection(deferred_fields):
# then load all of them
fields = fields.union(deferred_fields)
super(ExampleModel, self).refresh_from_db(using, fields, **kwargs)
Model.get_deferred_fields()
New in Django 1.8.
一個(gè)輔助方法,它返回一個(gè)集合,包含模型當(dāng)前所有延遲字段的屬性名稱。
驗(yàn)證一個(gè)模型涉及三個(gè)步驟:
Model.clean_fields()Model.clean()Model.validate_unique()當(dāng)你調(diào)用模型的full_clean() 方法時(shí),這三個(gè)方法都將執(zhí)行。
當(dāng)你使用ModelForm時(shí),is_valid() 將為表單中的所有字段執(zhí)行這些驗(yàn)證。更多信息參見(jiàn)ModelForm 文檔。 如果你計(jì)劃自己處理驗(yàn)證出現(xiàn)的錯(cuò)誤,或者你已經(jīng)將需要驗(yàn)證的字段從ModelForm 中去除掉,你只需調(diào)用模型的full_clean() 方法。
Model.full_clean(exclude=None, validate_unique=True)
該方法按順序調(diào)用Model.clean_fields()、Model.clean() 和Model.validate_unique()(如果validate_unique 為True),并引發(fā)一個(gè)ValidationError,該異常的message_dict 屬性包含三個(gè)步驟的所有錯(cuò)誤。
可選的exclude 參數(shù)用來(lái)提供一個(gè)可以從驗(yàn)證和清除中排除的字段名稱的列表。ModelForm 使用這個(gè)參數(shù)來(lái)排除表單中沒(méi)有出現(xiàn)的字段,使它們不需要驗(yàn)證,因?yàn)橛脩魺o(wú)法修正這些字段的錯(cuò)誤。
注意,當(dāng)你調(diào)用模型的save() 方法時(shí),full_clean()不會(huì) 自動(dòng)調(diào)用。如果你想一步就可以為你手工創(chuàng)建的模型運(yùn)行驗(yàn)證,你需要手工調(diào)用它。例如:
from django.core.exceptions import ValidationError
try:
article.full_clean()
except ValidationError as e:
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programmatically.
pass
full_clean() 第一步執(zhí)行的是驗(yàn)證每個(gè)字段。
Model.clean_fields(exclude=None)
這個(gè)方法將驗(yàn)證模型的所有字段??蛇x的exclude 參數(shù)讓你提供一個(gè)字段名稱列表來(lái)從驗(yàn)證中排除。如果有字段驗(yàn)證失敗,它將引發(fā)一個(gè)ValidationError。
full_clean() 第二步執(zhí)行的是調(diào)用Model.clean()。如要實(shí)現(xiàn)模型自定義的驗(yàn)證,應(yīng)該覆蓋這個(gè)方法。
Model.clean()
應(yīng)該用這個(gè)方法來(lái)提供自定義的模型驗(yàn)證,以及修改模型的屬性。例如,你可以使用它來(lái)給一個(gè)字段自動(dòng)提供值,或者用于多個(gè)字段需要一起驗(yàn)證的情形:
import datetime
from django.core.exceptions import ValidationError
from django.db import models
class Article(models.Model):
...
def clean(self):
# Don't allow draft entries to have a pub_date.
if self.status == 'draft' and self.pub_date is not None:
raise ValidationError('Draft entries may not have a publication date.')
# Set the pub_date for published items if it hasn't been set already.
if self.status == 'published' and self.pub_date is None:
self.pub_date = datetime.date.today()
然而請(qǐng)注意,和Model.full_clean() 類似,調(diào)用模型的save() 方法時(shí)不會(huì)引起clean() 方法的調(diào)用。
在上面的示例中,Model.clean() 引發(fā)的ValidationError 異常通過(guò)一個(gè)字符串實(shí)例化,所以它將被保存在一個(gè)特殊的錯(cuò)誤字典鍵NON_FIELD_ERRORS中。這個(gè)鍵用于整個(gè)模型出現(xiàn)的錯(cuò)誤而不是一個(gè)特定字段出現(xiàn)的錯(cuò)誤:
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
article.full_clean()
except ValidationError as e:
non_field_errors = e.message_dict[NON_FIELD_ERRORS]
若要引發(fā)一個(gè)特定字段的異常,可以使用一個(gè)字典實(shí)例化ValidationError,其中字典的鍵為字段的名稱。我們可以更新前面的例子,只引發(fā)pub_date 字段上的異常:
class Article(models.Model):
...
def clean(self):
# Don't allow draft entries to have a pub_date.
if self.status == 'draft' and self.pub_date is not None:
raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
...
最后,full_clean() 將檢查模型的唯一性約束。
Model.validate_unique(exclude=None)
該方法與clean_fields() 類似,只是驗(yàn)證的是模型的所有唯一性約束而不是單個(gè)字段的值??蛇x的exclude 參數(shù)允許你提供一個(gè)字段名稱的列表來(lái)從驗(yàn)證中排除。如果有字段驗(yàn)證失敗,將引發(fā)一個(gè) ValidationError。
注意,如果你提供一個(gè)exclude 參數(shù)給validate_unique(),任何涉及到其中一個(gè)字段的unique_together 約束將不檢查。
將一個(gè)對(duì)象保存到數(shù)據(jù)庫(kù),需要調(diào)用 save()方法:
Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None])
如果你想要自定義保存的動(dòng)作,你可以重寫 save() 方法。請(qǐng)看 重寫預(yù)定義的模型方法 了解更多細(xì)節(jié)。
模型保存過(guò)程還有一些細(xì)節(jié)的地方要注意;請(qǐng)看下面的章節(jié)。
如果模型具有一個(gè)AutoField —— 一個(gè)自增的主鍵 —— 那么該自增的值將在第一次調(diào)用對(duì)象的save() 時(shí)計(jì)算并保存:
>>> 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.
在調(diào)用save() 之前無(wú)法知道ID 的值,因?yàn)檫@個(gè)值是通過(guò)數(shù)據(jù)庫(kù)而不是Django 計(jì)算。
為了方便,默認(rèn)情況下每個(gè)模型都有一個(gè)AutoField 叫做id,除非你顯式指定模型某個(gè)字段的 primary_key=True。更多細(xì)節(jié)參見(jiàn)AutoField 的文檔。
Model.pk
無(wú)論你是自己定義還是讓Django 為你提供一個(gè)主鍵字段, 每個(gè)模型都將具有一個(gè)屬性叫做pk。它的行為類似模型的一個(gè)普通屬性,但實(shí)際上是模型主鍵字段屬性的別名。你可以讀取并設(shè)置它的值,就和其它屬性一樣,它會(huì)更新模型中正確的值。
如果模型具有一個(gè)AutoField,但是你想在保存時(shí)顯式定義一個(gè)新的對(duì)象ID,你只需要在保存之前顯式指定它而不用依賴ID 自動(dòng)分配的值:
>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b3.id # Returns 3.
>>> b3.save()
>>> b3.id # Returns 3.
如果你手工賦值一個(gè)自增主鍵的值,請(qǐng)確保不要使用一個(gè)已經(jīng)存在的主鍵值!如果你使用數(shù)據(jù)庫(kù)中已經(jīng)存在的主鍵值創(chuàng)建一個(gè)新的對(duì)象,Django 將假設(shè)你正在修改這個(gè)已存在的記錄而不是創(chuàng)建一個(gè)新的記錄。
接著上面的'Cheddar Talk' 博客示例,下面這個(gè)例子將覆蓋數(shù)據(jù)庫(kù)中之前的記錄:
b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
b4.save() # Overrides the previous blog with ID=3!
出現(xiàn)這種情況的原因,請(qǐng)參見(jiàn)下面的Django 如何知道是UPDATE 還是INSERT。
顯式指定自增主鍵的值對(duì)于批量保存對(duì)象最有用,但你必須有信心不會(huì)有主鍵沖突。
當(dāng)你保存一個(gè)對(duì)象時(shí),Django 執(zhí)行以下步驟:
1. 發(fā)出一個(gè)pre-save 信號(hào)。 發(fā)送一個(gè)django.db.models.signals.pre_save 信號(hào),以允許監(jiān)聽該信號(hào)的函數(shù)完成一些自定義的動(dòng)作。
2. 預(yù)處理數(shù)據(jù)。 如果需要,對(duì)對(duì)象的每個(gè)字段進(jìn)行自動(dòng)轉(zhuǎn)換。
大部分字段不需要預(yù)處理 —— 字段的數(shù)據(jù)將保持原樣。預(yù)處理只用于具有特殊行為的字段。例如,如果你的模型具有一個(gè)auto_now=True 的DateField,那么預(yù)處理階段將修改對(duì)象中的數(shù)據(jù)以確保該日期字段包含當(dāng)前的時(shí)間戳。(我們的文檔還沒(méi)有所有具有這種“特殊行為”字段的一個(gè)列表。)
3. 準(zhǔn)備數(shù)據(jù)庫(kù)數(shù)據(jù)。 要求每個(gè)字段提供的當(dāng)前值是能夠?qū)懭氲綌?shù)據(jù)庫(kù)中的類型。
大部分字段不需要數(shù)據(jù)準(zhǔn)備。簡(jiǎn)單的數(shù)據(jù)類型,例如整數(shù)和字符串,是可以直接寫入的Python 對(duì)象。但是,復(fù)雜的數(shù)據(jù)類型通常需要一些改動(dòng)。
例如,DateField 字段使用Python 的 datetime 對(duì)象來(lái)保存數(shù)據(jù)。數(shù)據(jù)庫(kù)保存的不是datetime 對(duì)象,所以該字段的值必須轉(zhuǎn)換成ISO兼容的日期字符串才能插入到數(shù)據(jù)庫(kù)中。
4. 插入數(shù)據(jù)到數(shù)據(jù)庫(kù)中。 將預(yù)處理過(guò)、準(zhǔn)備好的數(shù)據(jù)組織成一個(gè)SQL 語(yǔ)句用于插入數(shù)據(jù)庫(kù)。
5. 發(fā)出一個(gè)post-save 信號(hào)。 發(fā)送一個(gè)django.db.models.signals.post_save 信號(hào),以允許監(jiān)聽聽信號(hào)的函數(shù)完成一些自定義的動(dòng)作。
你可能已經(jīng)注意到Django 數(shù)據(jù)庫(kù)對(duì)象使用同一個(gè)save() 方法來(lái)創(chuàng)建和改變對(duì)象。Django 對(duì)INSERT 和UPDATE SQL 語(yǔ)句的使用進(jìn)行抽象。當(dāng)你調(diào)用save() 時(shí),Django 使用下面的算法:
True 的值(例如,非None 值或非空字符串),Django 將執(zhí)行UPDATE。UPDATE 沒(méi)有更新任何記錄,Django 將執(zhí)行INSERT。現(xiàn)在應(yīng)該明白了,當(dāng)保存一個(gè)新的對(duì)象時(shí),如果不能保證主鍵的值沒(méi)有使用,你應(yīng)該注意不要顯式指定主鍵值。關(guān)于這個(gè)細(xì)微差別的更多信息,參見(jiàn)上文的顯示指定主鍵的值 和下文的強(qiáng)制使用INSERT 或UPDATE。
在Django 1.5 和更早的版本中,在設(shè)置主鍵的值時(shí),Django 會(huì)作一個(gè) SELECT。如果SELECT 找到一行,那么Django 執(zhí)行UPDATE,否則執(zhí)行INSERT。舊的算法導(dǎo)致UPDATE 情況下多一次查詢。有極少數(shù)的情況,數(shù)據(jù)庫(kù)不會(huì)報(bào)告有一行被更新,即使數(shù)據(jù)庫(kù)包含該對(duì)象的主鍵值。有個(gè)例子是PostgreSQL 的ON UPDATE 觸發(fā)器,它返回NULL。在這些情況下,可能要通過(guò)將select_on_save 選項(xiàng)設(shè)置為True 以啟用舊的算法。
在一些很少見(jiàn)的場(chǎng)景中,需要強(qiáng)制save() 方法執(zhí)行SQL 的 INSERT 而不能執(zhí)行UPDATE。或者相反:更新一行而不是插入一個(gè)新行。在這些情況下,你可以傳遞force_insert=True 或 force_update=True 參數(shù)給save() 方法。顯然,兩個(gè)參數(shù)都傳遞是錯(cuò)誤的:你不可能同時(shí)插入和更新!
你應(yīng)該極少需要使用這些參數(shù)。Django 幾乎始終會(huì)完成正確的事情,覆蓋它將導(dǎo)致錯(cuò)誤難以跟蹤。這個(gè)功能只用于高級(jí)用法。
使用update_fields 將強(qiáng)制使用類似force_update 的更新操作。
有時(shí)候你需要在一個(gè)字段上執(zhí)行簡(jiǎn)單的算法操作,例如增加或者減少當(dāng)前值。實(shí)現(xiàn)這點(diǎn)的簡(jiǎn)單方法是像下面這樣:
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()
如果從數(shù)據(jù)庫(kù)中讀取的舊的number_sold 值為10,那么寫回到數(shù)據(jù)庫(kù)中的值將為11。
通過(guò)將更新基于原始字段的值而不是顯式賦予一個(gè)新值,這個(gè)過(guò)程可以避免競(jìng)態(tài)條件而且更快。Django 提供F 表達(dá)式 用于這種類型的相對(duì)更新。利用F 表達(dá)式,前面的示例可以表示成:
>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()
更多細(xì)節(jié),請(qǐng)參見(jiàn)F 表達(dá)式 和它們在更新查詢中的用法。
如果傳遞給save() 的update_fields 關(guān)鍵字參數(shù)一個(gè)字段名稱列表,那么將只有該列表中的字段會(huì)被更新。如果你想更新對(duì)象的一個(gè)或幾個(gè)字段,這可能是你想要的。不讓模型的所有字段都更新將會(huì)帶來(lái)一些輕微的性能提升。例如:
product.name = 'Name changed again'
product.save(update_fields=['name'])
update_fields 參數(shù)可以是任何包含字符串的可迭代對(duì)象??盏?code>update_fields 可迭代對(duì)象將會(huì)忽略保存。如果為None 值,將執(zhí)行所有字段上的更新。
指定update_fields 將強(qiáng)制使用更新操作。
當(dāng)保存通過(guò)延遲模型加載(only() 或defer())進(jìn)行訪問(wèn)的模型時(shí),只有從數(shù)據(jù)庫(kù)中加載的字段才會(huì)得到更新。這種情況下,有個(gè)自動(dòng)的update_fields。如果你賦值或者改變延遲字段的值,該字段將會(huì)添加到更新的字段中。
Model.delete([using=DEFAULT_DB_ALIAS])
發(fā)出一個(gè)SQL DELETE 操作。它只在數(shù)據(jù)庫(kù)中刪除這個(gè)對(duì)象;其Python 實(shí)例仍將存在并持有各個(gè)字段的數(shù)據(jù)。
更多細(xì)節(jié),包括如何批量刪除對(duì)象,請(qǐng)參見(jiàn)刪除對(duì)象。
如果你想自定義刪除的行為,你可以覆蓋delete() 方法。詳見(jiàn)覆蓋預(yù)定義的模型方法。
當(dāng)你pickle 一個(gè)模型時(shí),它的當(dāng)前狀態(tài)是pickled。當(dāng)你unpickle 它時(shí),它將包含pickle 時(shí)模型的實(shí)例,而不是數(shù)據(jù)庫(kù)中的當(dāng)前數(shù)據(jù)。
你不可以在不同版本之間共享pickles
模型的Pickles 只對(duì)于產(chǎn)生它們的Django 版本有效。如果你使用Django 版本N pickle,不能保證Django 版本N+1 可以讀取這個(gè)pickle。Pickles 不應(yīng)該作為長(zhǎng)期的歸檔策略。
New in Django 1.8.因?yàn)閜ickle 兼容性的錯(cuò)誤很難診斷例如一個(gè)悄無(wú)聲息損壞的對(duì)象,當(dāng)你unpickle 模型使用的Django 版本與pickle 時(shí)的不同將引發(fā)一個(gè)
RuntimeWarning。
有幾個(gè)實(shí)例方法具有特殊的目的。
注
在Python 3 上,因?yàn)樗械淖侄味荚徽J(rèn)為是Unicode,只需使用
__str__()方法(__unicode__()方法被廢棄)。如果你想與Python 2 兼容,你可以使用python_2_unicode_compatible()裝飾你的模型類。
Model.__unicode__()
__unicode__() 方法在每當(dāng)你對(duì)一個(gè)對(duì)象調(diào)用unicode() 時(shí)調(diào)用。Django 在許多地方都使用unicode(obj)(或者相關(guān)的函數(shù) str(obj))。最明顯的是在Django 的Admin 站點(diǎn)顯示一個(gè)對(duì)象和在模板中插入對(duì)象的值的時(shí)候。所以,你應(yīng)該始終讓__unicode__() 方法返回模型的一個(gè)友好的、人類可讀的形式。
例如:
from django.db import models
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)
如果你定義了模型的__unicode__() 方法且沒(méi)有定義__str__() 方法,Django 將自動(dòng)提供一個(gè) __str__(),它調(diào)用__unicode__() 并轉(zhuǎn)換結(jié)果為一個(gè)UTF-8 編碼的字符串。下面是一個(gè)建議的開發(fā)實(shí)踐:只定義__unicode__() 并讓Django 在需要時(shí)負(fù)責(zé)字符串的轉(zhuǎn)換。
Model.__str__()
__str__() 方法在每當(dāng)你對(duì)一個(gè)對(duì)象調(diào)用str() 時(shí)調(diào)用。在Python 3 中,Django 在許多地方使用str(obj)。 最明顯的是在Django 的Admin 站點(diǎn)顯示一個(gè)對(duì)象和在模板中插入對(duì)象的值的時(shí)候。 所以,你應(yīng)該始終讓__str__() 方法返回模型的一個(gè)友好的、人類可讀的形式。
例如:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
在Python 2 中,Django 內(nèi)部對(duì)__str__ 的直接使用主要在隨處可見(jiàn)的模型的repr() 輸出中(例如,調(diào)試時(shí)的輸出)。如果已經(jīng)有合適的__unicode__() 方法就不需要__str__() 了。
前面__unicode__() 的示例可以使用__str__() 這樣類似地編寫:
from django.db import models
from django.utils.encoding import force_bytes
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.force_bytes() here because
# first_name and last_name will be unicode strings.
return force_bytes('%s %s' % (self.first_name, self.last_name))
Model.__eq__()
定義這個(gè)方法是為了讓具有相同主鍵的相同實(shí)類的實(shí)例是相等的。對(duì)于代理模型,實(shí)類是模型第一個(gè)非代理父類;對(duì)于其它模型,它的實(shí)類就是模型類自己。
例如:
from django.db import models
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
class MyProxyModel(MyModel):
class Meta:
proxy = True
class MultitableInherited(MyModel):
pass
MyModel(id=1) == MyModel(id=1)
MyModel(id=1) == MyProxyModel(id=1)
MyModel(id=1) != MultitableInherited(id=1)
MyModel(id=1) != MyModel(id=2)
Changed in Django 1.7:
在之前的版本中,只有類和主鍵都完全相同的實(shí)例才是相等的。
Model.__hash__()
__hash__ 方法基于實(shí)例主鍵的值。它等同于hash(obj.pk)。如果實(shí)例的主鍵還沒(méi)有值,將引發(fā)一個(gè)TypeError(否則,__hash__ 方法在實(shí)例保存的前后將返回不同的值,而改變一個(gè)實(shí)例的__hash__ 值在Python 中是禁止的)。
Changed in Django 1.7:
在之前的版本中,主鍵沒(méi)有值的實(shí)例是可以哈希的。
Model.get_absolute_url()
get_absolute_url() 方法告訴Django 如何計(jì)算對(duì)象的標(biāo)準(zhǔn)URL。對(duì)于調(diào)用者,該方法返回的字符串應(yīng)該可以通過(guò)HTTP 引用到這個(gè)對(duì)象。
例如:
def get_absolute_url(self):
return "/people/%i/" % self.id
(雖然這段代碼正確又簡(jiǎn)單,這并不是編寫這個(gè)方法可移植性最好的方式。通常使用reverse() 函數(shù)是最好的方式。)
例如:
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('people.views.details', args=[str(self.id)])
Django 使用get_absolute_url() 的一個(gè)地方是在Admin 應(yīng)用中。如果對(duì)象定義該方法,對(duì)象編輯頁(yè)面將具有一個(gè)“View on site”鏈接,可以將你直接導(dǎo)入由get_absolute_url() 提供的對(duì)象公開視圖。
類似地,Django 的另外一些小功能,例如syndication feed 框架 也使用get_absolute_url()。 如果模型的每個(gè)實(shí)例都具有一個(gè)唯一的URL 是合理的,你應(yīng)該定義get_absolute_url()。
警告
你應(yīng)該避免從沒(méi)有驗(yàn)證過(guò)的用戶輸入構(gòu)建URL,以減少有害的鏈接和重定向:
def get_absolute_url(self):
return '/%s/' % self.name
如果
self.name為'/example.com',將返回 '//example.com/', 而它是一個(gè)合法的相對(duì)URL而不是期望的'/%2Fexample.com/'。
在模板中使用get_absolute_url() 而不是硬編碼對(duì)象的URL 是很好的實(shí)踐。例如,下面的模板代碼很糟糕:
<!-- BAD template code. Avoid! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>
下面的模板代碼要好多了:
<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
如果你改變了對(duì)象的URL 結(jié)構(gòu),即使是一些簡(jiǎn)單的拼寫錯(cuò)誤,你不需要檢查每個(gè)可能創(chuàng)建該URL 的地方。在get_absolute_url() 中定義一次,然后在其它代碼調(diào)用它。
注
get_absolute_url()返回的字符串必須只包含ASCII 字符(URI 規(guī)范RFC 2396 的要求),并且如需要必須要URL-encoded。代碼和模板中對(duì)
get_absolute_url()的調(diào)用應(yīng)該可以直接使用而不用做進(jìn)一步處理。你可能想使用django.utils.encoding.iri_to_uri()函數(shù)來(lái)幫助你解決這個(gè)問(wèn)題,如果你正在使用ASCII 范圍之外的Unicode 字符串。
除了save()、delete()之外,模型的對(duì)象還可能具有以下一些方法:
Model.get_FOO_display()
對(duì)于每個(gè)具有choices 的字段,每個(gè)對(duì)象將具有一個(gè)get_FOO_display() 方法,其中FOO 為該字段的名稱。這個(gè)方法返回該字段對(duì)“人類可讀”的值。
例如:
from django.db import models
class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
Model.get_next_by_FOO(**kwargs)
Model.get_previous_by_FOO(**kwargs)
如果DateField 和DateTimeField沒(méi)有設(shè)置 null=True,那么該對(duì)象將具有get_next_by_FOO() 和get_previous_by_FOO() 方法,其中FOO 為字段的名稱。它根據(jù)日期字段返回下一個(gè)和上一個(gè)對(duì)象,并適時(shí)引發(fā)一個(gè)DoesNotExist。
這兩個(gè)方法都將使用模型默認(rèn)的管理器來(lái)執(zhí)行查詢。如果你需要使用自定義的管理器或者你需要自定義的篩選,這個(gè)兩個(gè)方法還接受可選的參數(shù),它們應(yīng)該用字段查詢 中提到的格式。
注意,對(duì)于完全相同的日期,這些方法還將利用主鍵來(lái)進(jìn)行查找。這保證不會(huì)有記錄遺漏或重復(fù)。這還意味著你不可以在未保存的對(duì)象上使用這些方法。
exception Model.DoesNotExist
ORM 在好幾個(gè)地方會(huì)引發(fā)這個(gè)異常,例如QuerySet.get() 根據(jù)給定的查詢參數(shù)找不到對(duì)象時(shí)。
Django 為每個(gè)類提供一個(gè)DoesNotExist 異常屬性是為了區(qū)別找不到的對(duì)象所屬的類,并讓你可以利用try/except捕獲一個(gè)特定模型的類。這個(gè)異常是django.core.exceptions.ObjectDoesNotExist 的子類。
譯者:Django 文檔協(xié)作翻譯小組,原文:Instance methods。
本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。
Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。