Django 文檔協做翻譯小組人手緊缺,有興趣的朋友能夠加入咱們,徹底公益性質。html
交流羣:467338606python
模型是有關你的數據的,簡單、肯定的信息源。它包含了你所儲存數據的一些必要的字段和行爲。一般來講,每一個模型都對應數據庫中的一張表。django
基礎:app
每一個模型都是django.db.models.Model類的子類。ide
模型的每一個屬性都表示數據庫中的一個字段。網站
Django 會提供一套自動生成的用於數據庫訪問的API;詳見執行查詢。ui
這個例子定義了一個Person模型,它有 first_name和last_name兩個屬性this
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
first_name和last_name是模型的兩個字段。每一個字段都被指定成一個類屬性,每一個屬性 都映射一個數據庫的列。編碼
上面的Person模型會在數據庫中建立這樣一張表:
CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
一些技術上的注意事項:字段類型
這個表的名稱myapp_person,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱,詳見Table names。
id 字段是自動添加的,但這個行爲能夠被重寫。詳見Automatic primary key fields。
這個例子使用 PostgreSQL 語法格式化CREATE TABLESQL 語句,要注意的是 Django 是根據settings file配置中指定的數據庫類型來生成相應的 SQL 語句。
一旦你定義了模型,就要通知Django啓用這些模型,你要作的就是修改配置文件中的INSTALLED_APPS 設置,在其中添加models.py所在應用的名稱。
例如,假設你的 model 定義在 mysite.myapp.models 中 ( mysite 這個包是由 manage.py startapp 腳本建立的),那麼 INSTALLED_APPS 就應該包含下面這行:
INSTALLED_APPS = ( #... 'mysite.myapp', #... )
在 INSTALLED_APPS 中添加新應用以後,要運行 manage.py syncdb 同步數據庫。
模型 中不可或缺且最爲重要的,就是字段集,它是一組數據庫字段的列表。字段被指定爲類屬性。要注意選擇字段名稱的時候不要和models API 衝突,好比clean, save, 或者delete。
例如:
class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician) name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField()
model 中的每一個字段都是 Field 子類的某個實例。Django 根據字段類的類型肯定如下信息:
數據庫當中的列類型 (好比,INTEGER, VARCHAR)。
Django 的用戶管理界面所使用的部件(widget)。固然,前提是你啓用了 Django 的管理後臺 (例如, <input type="text">
, <select>
)。
最低限度的驗證需求。它被用在 Django 管理後臺和自動生成的表單中。
Django 自帶數十種內置的字段類型;詳見 model 字段參考(model field reference)。若是內置類型仍不能知足你的要求,你能夠自由地編寫符合你要求的字段類型;詳見 編寫自定義 model 字段(Writing custom model fields)。
每一個字段都有一些特有的參數,詳見 model 字段參考(model field reference)。例如, CharField (還有它的派生類) 都須要 max_length 參數來指定存儲數據的 VARCHAR 數據庫字段的大小。
還有一些適用於全部字段的可選的通用參數,這些參數在 參考(reference) 中有詳細定義,這裏咱們只簡單介紹一些最經常使用的:
null
若是爲 True, Django 在數據庫中會將空值(empty)存儲爲 NULL 。 默認爲 False。
blank
若是爲 True,該字段容許不填(blank)。默認爲 False。
要注意,這與 null 不一樣。 null 純粹是數據庫範疇的,而 blank 是數據驗證範疇的。若是一個字段的 blank=True,Django 的管理後臺在作數據驗證時,會容許該字段是空值。若是字段的 blank=False,該字段就是必填的。
choices
它是一個可迭代的二元組(例如,列表或是元組),用來給字段提供選擇項。若是設置了 choices ,Django 的管理後臺就會顯示選擇框,而不是標準的文本框,並且這個選擇框的選項就是 choices 中的元組。
這是一個關於 choices 列表的例子:
YEAR_IN_SCHOOL_CHOICES = ( (u'FR', u'Freshman'), (u'SO', u'Sophomore'), (u'JR', u'Junior'), (u'SR', u'Senior'), (u'GR', u'Graduate'), )
每一個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是在管理界面或 ModelChoiceField 中用做顯示的內容。在一個給定的 model 類的實例中,想獲得某個 choices 字段的顯示值,就調用 get_FOO_display 方法(這裏的 FOO 就是 choices 字段的名稱 )。例如:
from django.db import models class Person(models.Model): GENDER_CHOICES = ( (u'M', u'Male'), (u'F', u'Female'), ) name = models.CharField(max_length=60) gender = models.CharField(max_length=2, choices=GENDER_CHOICES) >>> p = Person(name="Fred Flinstone", gender="M") >>> p.save() >>> p.gender u'M' >>> p.get_gender_display() u'Male'
default
字段的默認值。它能夠是一個值,也能夠是一個可調用的對象(這裏稱之爲對象C)。如果後者,那麼每次建立一個新對象時,對象C都將被調用。
help_text
附加的幫助信息。在管理後臺編輯該對象的表單中,它顯示在字段下面。即便你的對象無須在後臺進行管理,它對於文檔化也是頗有用的。
primary_key
若是爲 True,那麼這個字段就是 model 的主鍵。
若是你沒有指定任何一個字段的 primary_key=True,Django 就會自動添加一個 IntegerField 字段作爲主鍵。因此除非你想重寫默認的主鍵方法,不然不必在任何字段上設置 primary_key=True 。詳見 自增主鍵字段(Automatic primary key fields).
主鍵字段是隻讀的。若是你在一個已存在的對象上面更改主鍵的值而且保存,一個新的對象將會在原有對象以外建立出來。例如:
from django.db import models class Fruit(models.Model): name = models.CharField(max_length=100, primary_key=True) >>> fruit = Fruit.objects.create(name='Apple') >>> fruit.name = 'Pear' >>> fruit.save() >>> Fruit.objects.values_list('name', flat=True) ['Apple', 'Pear']
unique
若是爲 True,那麼字段值就必須是全表惟一的。
再說一次,這些僅僅是經常使用字段的簡短介紹,要了解詳細內容,請查看 通用 model 字段選項參考(common model field option reference).
默認狀況下,Django 會給每一個 model 添加下面這個字段:
id = models.AutoField(primary_key=True)
這是一個自增主鍵字段。
若是你想指定一個自定義主鍵字段,只要在某個字段上指定 primary_key=True 便可。若是 Django 看到你顯式地設置了 Field.primary_key,就不會自動添加 id 列。
每一個 model 只要有一個字段指定 primary_key=True 就能夠了。(不管是顯式聲明仍是自動添加的。)
除了 ForeignKey, ManyToManyField 和 OneToOneField 以外,其他每一個字段類型都接受一個排在首位的可選的位置參數--這就是字段的自述名。若是沒有給定自述名,Django 將根據字段的屬性名稱自動建立自述名--就是將屬性名稱的空格替換成下劃線。
在這個例子中,自述名是 "Person's first name":
first_name = models.CharField("Person's first name", max_length=30)
在這個例子中,自述名是 "first name":
first_name = models.CharField(max_length=30)
ForeignKey, ManyToManyField 和 OneToOneField 都要求排在首位的參數得是一個 model 類,因此要使用 verbose_name 關鍵字參數才能指定自述名:
poll = models.ForeignKey(Poll, verbose_name="the related poll") sites = models.ManyToManyField(Site, verbose_name="list of sites") place = models.OneToOneField(Place, verbose_name="related place")
verbose_name 首字母是不用大寫的,這是由於 Django 在必要的時候會自動大寫首字母的。
顯然,關係數據庫的威力體如今表之間的相互關聯。Django 提供了三種最多見的數據庫關係:多對一(many-to-one),多對多(many-to-many),一對一(one-to-one)。
Django 使用 ForeignKey 定義多對一關係。 和使用其餘 字段(Field) 類型同樣:在 model 當中把它作爲一個類屬性包含進來。
ForeignKey 須要一個位置參數:與該 model 關聯的類。
好比,若是每一個 汽車(Car) model 都有一個 生產商(Manufacturer) model -- 也就是說,一個 Manufacturer 能夠生產出不少 Car ;可是每一輛 Car 卻只能有一個 Manufacturer -- 使用下面的定義:
class Manufacturer(models.Model): # ... class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer) # ...
你還能夠建立 遞歸的關聯關係(recursive relationships) (對象和本身進行多對一關聯) 和 關聯至還沒有定義關係的 model (relationships to models not yet defined); 詳見 model 字段參考(the model field reference) 。
建議你用被關聯 model 的小寫名稱作爲 ForeignKey 字段的命名 (上例中,咱們就是以manufacturer 的小寫作爲命名的)。固然,你也能夠起別的名字,例如:
class Car(models.Model): company_that_makes_it = models.ForeignKey(Manufacturer) # ...
另見
ForeignKey 字段還能夠接受別的參數,它們都是可選的,在 model 字段參考(the model field reference) 有詳細介紹。這些選項定義了關係是如何工做的。
訪問反向關聯對象的細節,請見Following relationships backward example。
示例代碼請見多對一關係的模型例子( Many-to-one relationship model example)。
ManyToManyField 用來定義多對多關係,用法和其餘 Field 字段類型同樣:在 model 中作爲一個類屬性包含進來。
ManyToManyField 須要一個位置參數:和該 model 關聯的類。
例如,一個 匹薩(Pizza) 能夠有多種不一樣口味的 澆頭(Topping) -- 也就是說,一種 Topping 能夠澆在多個 Pizza 上,而每一個 Pizza 也能夠澆上多種 topping -- 以下:
class Topping(models.Model): # ... class Pizza(models.Model): # ... toppings = models.ManyToManyField(Topping)
和使用 ForeignKey 同樣,你也能夠建立 遞歸的關聯關係(recursive relationships) (對象和本身作多對多關聯)和 關聯至還沒有定義關係的 model (relationships to models not yet defined);詳見 the model field reference 。
建議你以被關聯 model 名稱的複數形式作爲 ManyToManyField 的命名 (例如上例中的 toppings )。
在哪一個 model 中設置 ManyToManyField 並不重要,在兩個 model 中任選一個便可。
一般來講,若是啓用了 Django 管理後臺,你就能夠在後臺將 ManyToManyField 實例添加到關聯對象中。在上面的例子中,在 Pizza 裏面設置 toppings (而不是在 Topping 裏面設置 pizzas ManyToManyField)。 這麼設置的緣由是由於一個 pizza 有多個 topping 相比於一個 topping 澆在多個 pizza 上要更加天然。這樣,在 Pizza 的管理後臺中,就會容許用戶選擇不一樣的 toppings。
另見
在 多對多關係 model 實例(Many-to-many relationship model example) 有一個完整例子。
ManyToManyField 字段還能夠接受別的參數,它們都是可選的,在 model 字段參考(the model field reference) 中有詳細介紹。這些選項定義了關係是如何工做的。
處理相似搭配 pizza 和 topping 這樣簡單的多對多關係時,使用標準的 ManyToManyField 就能夠了。可是有時,咱們須要在兩個 model 之間關聯其餘的數據。
例如,有這樣一個應用:關注某個音樂小組,它擁有多個音樂家成員。咱們能夠用一個標準的 ManyToManyField 表示小組和成員之間的多對多關係。可是,有時你可能想知道更多成員關係的細節,好比成員是什麼時候加入小組的。
在這種狀況下,Django 容許你指定一個 model 來定義多對多關係(咱們稱之爲中介 model )。你能夠將其餘字段放在中介 model 裏面,而主 model 的 ManyToManyField 使用 through 參數來指向中介 model 。對於上面的音樂小組的例子來講,代碼以下:
class Person(models.Model): name = models.CharField(max_length=128) def __unicode__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __unicode__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person) group = models.ForeignKey(Group) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
在設置中介 model 時,要顯式地定義一個外鍵,它與包含多對多關係的 model 相關聯。這個顯式的聲明定義了兩個 model 之間中如何關聯的。
在使用中介 model 時要注意如下限制:
有且只有一個外鍵指向目標 model (例中目標 model 就是 Person );不然就會拋出驗證異常。
有且只有一個外鍵指向源 model (例中源 model 就是 Group );不然就會拋出驗證異常。
但存在惟一的一種特殊狀況:利用中介 model 實現遞歸的多對多關係。這種狀況下,兩個外鍵指向同一個 model 是容許的;但這個 model 會被視爲多對多關係中不一樣的雙方進行處理。
定義遞歸的多對多關係時,你必須設置 symmetrical=False (詳見 model 字段參考(the model field reference))。
如今你已經設置了 ManyToManyField 來使用中介 model (在這個例子中就是 Membership),接下來你要開始建立多對多關係。你要作的就是建立中介 model 的實例:
>>> ringo = Person.objects.create(name="Ringo Starr") >>> paul = Person.objects.create(name="Paul McCartney") >>> beatles = Group.objects.create(name="The Beatles") >>> m1 = Membership(person=ringo, group=beatles, ... date_joined=date(1962, 8, 16), ... invite_reason= "Needed a new drummer.") >>> m1.save() >>> beatles.members.all() [<Person: Ringo Starr>] >>> ringo.group_set.all() [<Group: The Beatles>] >>> m2 = Membership.objects.create(person=paul, group=beatles, ... date_joined=date(1960, 8, 1), ... invite_reason= "Wanted to form a band.") >>> beatles.members.all() [<Person: Ringo Starr>, <Person: Paul McCartney>]
與普通的多對多字段不一樣,你不能使用 add, create, 和賦值語句 (好比,beatles.members = [...]) 來建立關係:
# THIS WILL NOT WORK >>> beatles.members.add(john) # NEITHER WILL THIS >>> beatles.members.create(name="George Harrison") # AND NEITHER WILL THIS >>> beatles.members = [john, paul, ringo, george]
爲何不能這樣作? 這是由於你不能只建立 Person and a Group 之間的關聯關係,你還要指定 Membership model 中所須要的全部信息;而簡單的 add, create 和賦值語句是作不到這一點的。因此它們不能在這種狀況下使用。此時,惟一的辦法就是建立中介 model 的實例。
remove 方法被禁用也是出於一樣的緣由。可是 clear() 方法倒是可用的。它能夠清空某個實例全部的多對多關係:
# Beatles have broken up >>> beatles.members.clear()
在建立了中介 model 的實例,完成了對多對多關係的定義以後,你就能夠執行查詢了。和普通的多對多字段同樣,你能夠直接使用被關聯 model 的屬性進行查詢:
# Find all the groups with a member whose name starts with 'Paul' >>> Group.objects.filter(members__name__startswith='Paul') [<Group: The Beatles>]
若是你使用了中介 model ,你也能夠利用中介 model 的其餘屬性進行查詢:
# Find all the members of the Beatles that joined after 1 Jan 1961 >>> Person.objects.filter( ... group__name='The Beatles', ... membership__date_joined__gt=date(1961,1,1)) [<Person: Ringo Starr]
OneToOneField 用來定義一對一關係。用法和其餘 Field 字段類型同樣:在 model 裏面作爲類屬性包含進來。
當某個對象想擴展自另外一個對象時,最經常使用的方式就是在這個對象的主鍵上添加一對一關係。
OneToOneField 須要一個位置參數:與 model 關聯的類。
例如,你想建一個 "places" 數據庫,裏面有一些經常使用的字段,好比 address, phone number, 等等。接下來,若是你想在 Place 數據庫的基礎上創建一個 飯店(Restaurant) 數據庫,而不想將已有的字段複製到 Restaurant model ,那你能夠在 Restaurant 添加一個 OneToOneField 字段,這個字段指向 Place (由於飯店(restaurant)自己就是一個地點(place),事實上,在處理這個問題的時候,你已經使用了一個典型的 繼承(inheritance),它隱含了一個一對一關係)。
和使用 ForeignKey 同樣,你能夠定義 遞歸的關聯關係(recursive relationship) 和 引用還沒有定義關係的 model (references to as-yet undefined models) 。詳見 model 字段參考(the model field reference) 。
參見
在 一對一關係的 model 例子(One-to-one relationship model example) 有一套完整的例子。
這部分是在 Django 1.0 中新增的: 請查看版本文檔
OneToOneField 字段還有其餘一些參數,它們都是可選的,在 model 字段參考(model field reference) 中有詳細介紹。
在之前的版本中,OneToOneField 字段會自動變成 model 的主鍵。不過如今已經不這麼作了(不過要是你願意的話,你仍能夠傳遞 primary_key 參數來建立主鍵字段)。因此一個 model 中能夠有多個 OneToOneField 字段。
訪問其餘應用的 model 是很是容易的。在使用 model 以前將它導入到當前程序便可。例如:
from mysite.geography.models import ZipCode class Restaurant(models.Model): # ... zip_code = models.ForeignKey(ZipCode)
Django 對字段的命名只有兩個限制:
字段名不能夠是 Python 的保留字,不然會致使 Python 語法錯誤。例如:
class Example(models.Model): pass = models.IntegerField() # 'pass' is a reserved word!
字段名稱不能夠包含連續多個下劃線,由於這與 Django 查詢時所用的篩選條件語法相沖突。例如:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
可是,只要你的字段名稱與數據庫中的列名不一樣,就能夠繞過這些限制。詳見 db_column 選項。
SQL 保留字,如 join, where 和 select, 能夠作爲 model 中字段的名稱。這是由於 Django 會對每一個 SQL 查詢的數據庫名稱和列名稱作重編碼,至於如何編碼視你所用的數據庫而定。
若是 Django 自帶的字段類型不能知足你的應用,或者你但願使用一些不常見的數據庫列類型,那你能夠建立自定義的字段類型。詳見 編寫自定義 model 字段(Writing custom model fields)。
經過使用一個內含的 class Meta 來爲你的model 添加元數據,例如:
class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] verbose_name_plural = "oxen"
在 model 裏面,除了字段就是元數據,好比排序項(ordering),數據庫名稱(db_table),和自述名(verbose_name 和 verbose_name_plural)。對於 model 來講,這些都不是必需的,甚至就連 class Meta 自己都不是必需的。
Meta 選項的完整列表能夠在 model 選項參考(model option reference) 中找到。
自定義 model 的方法,就是爲你的對象添加自定義的行級功能(row-level),而 Manager 方法卻喜歡作表級的事情(table-wide)。因此,model 方法應該做用於 model 類的實例(也就是說,在實例對象上使用 model 方法,而不是在類上直接使用)。
最好是隻在一個地方(就是在 model 中)保存商業邏輯。
例如,在下面這個 model 中自定義方法:
from django.contrib.localflavor.us.models import USStateField class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) birth_date = models.DateField() address = models.CharField(max_length=100) city = models.CharField(max_length=50) state = USStateField() # Yes, this is America-centric... def baby_boomer_status(self): "Returns the person's baby-boomer status." import datetime if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31): return "Baby boomer" if self.birth_date < datetime.date(1945, 8, 1): return "Pre-boomer" return "Post-boomer" def is_midwestern(self): "Returns True if this person is from the Midwest." return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO') def _get_full_name(self): "Returns the person's full name." return '%s %s' % (self.first_name, self.last_name) full_name = property(_get_full_name)
本例中最後一個方法是一個 屬性(property). 瞭解屬性詳見這裏。
在 model 實例參考(model instance reference) 中一個完整的方法列表 自動添加到每一個 model 中的方法(methods automatically given to each model)。 你能夠重寫裏面的大部分方法 -- 詳見下面的 重寫已定義的 model 方法(overriding predefined model methods),-- 可是有兩個方法是常常要重寫的:
__unicode__()
這是一個 Python 的魔術方法 ("magic method"),它返回對象的 Unicode 表示。當某個對象被要強制轉換成字符串,或是要作爲字符串顯示時,Python 和 Django 就會調用該方法。最典型的,在命令行或管理後臺中顯示對象,就會用到 __unicode__() 方法。
你應該老是自定義這個方法;該方法默認的實現沒有什麼用。
get_absolute_url()
Django 使用這個方法算出某個對象的網址(URL)。Django 在管理後臺和任何須要獲得對象網址的地方使用該方法。
若是對象有一個惟一的網址,那麼你就應該定義這個方法。
還有另一組 model 方法(model methods) 封裝了你想定製的數據庫的操做。有些狀況下,你可能常常會改變 save() 和 delete() 的實現。
你能夠自由地重寫這些方法 (以及任何其餘的 model 方法) 來改變默認的實現。
一個典型的重寫內置方法的案例就是:在你保存對象時,觸發某些操做。例如 (詳見 save() 的參數說明):
class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, force_insert=False, force_update=False): do_something() super(Blog, self).save(force_insert, force_update) # Call the "real" save() method. do_something_else()
你也能夠阻止保存:
class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def save(self, force_insert=False, force_update=False): if self.name == "Yoko Ono's blog": return # Yoko shall never have her own blog! else: super(Blog, self).save(force_insert, force_update) # Call the "real" save() method.
別忘記調用父類的方法,這很重要 -- 上例中的父類方法是 super(Blog, self).save() ,它要作的就是確保將對象保存到數據庫。若是忘記調用父類的方法,默認的行爲就不會發生,也就不會對數據庫進行操做。
另一種常見的模式就是在 model 方法或是模塊級(module-level)的方法中使用定製的 SQL 語句。想了解使用原始 SQL 的更多細節,請查看 使用原始 SQL (using raw SQL) 。
這部分是在 Django 1.0 中新增的: 請注意版本文檔
Django 中的 model 繼承和 Python 中的類繼承很是類似,只不過你要選擇具體的實現方式:讓父 model 擁有獨立的數據庫;仍是讓父 model 只包含基本的公共信息,由子 model 呈現公共信息。
在 Django 中有三種繼承方式:
一般,你只是想用父 model 來保存那些你不想在子 model 中重複錄入的信息,父類並不單獨使用。 抽象基類(Abstract base classes) 適用於這種狀況。
若是你繼承了某個已有的 model (多是直接從其餘應用中拿來的),並想讓每一個 model 都有本身的數據庫。多表繼承(Multi-table inheritance) 適用於這種狀況。
最後,若是你只想在 model 中修改 Python-level 級的行爲,而不涉及字段改變。 代理 model (Proxy models) 適用於這種場合。
若是你想把某些公共信息添加到不少 model 中,抽象基類就顯得很是有用。你編寫完基類以後,在 Meta 內嵌類中設置 abstract=True ,該類就不能建立任何數據表。然而若是將它作爲其餘 model 的基類,那麼該類的字段就會被添加到子類中。抽象基類和子類若是含有同名字段,就會致使錯誤(Django 將拋出異常)。
舉個例子:
class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
學生(Student) model 會有三個字段: 姓名(name), 年齡(age) 和 分組(home_group)。 CommonInfo model 不能作爲普通的 Django model 使用,由於它是一個抽象基類。他即不生成數據表,也沒有 manager ,更不能直接被實例化和保存。
對不少應用來講,這種繼承方式正是你想要的。它提供一種在 Python 語言層級上提取公共信息的方式,但在數據庫層級上,各個子類仍然只建立一個數據庫。
建立抽象基類的時候,Django 會將你在基類中所聲明的有效的 Meta 內嵌類作爲一個屬性。若是子類沒有聲明它本身的 Meta 內嵌類,它就會繼承父類的 Meta 。子類的 Meta 也能夠直接繼承父類的 Meta 內嵌類,對其進行擴展。例如:
class CommonInfo(models.Model): ... class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): ... class Meta(CommonInfo.Meta): db_table = 'student_info'
繼承時,Django 會對基類的 Meta 內嵌類作一個調整:在安裝 Meta 屬性以前,Django 會設置 abstract=False。 這意味着抽象基類的子類不會自動變成抽象類。固然,你可讓一個抽象類繼承另外一個抽象基類,不過每次都要顯式地設置 abstract=True 。
對於抽象基類而言,有些屬性放在 Meta 內嵌類裏面是沒有意義的。例如,包含 db_table 將意味着全部的子類(是指那些沒有指定本身的 Meta 內嵌類的子類)都使用同一張數據表,通常來講,這並非咱們想要的。
若是你在 ForeignKey 或 ManyToManyField 字段上使用 related_name 屬性,你必須老是爲該字段指定一個惟一的反向名稱。但在抽象基類上這樣作就會引起一個很嚴重的問題。由於 Django 會將基類字段添加到每一個子類當中,而每一個子類的字段屬性值都徹底相同 (這裏面就包括 related_name)。注:這樣每一個子類的關聯字段都會指向同一個字段。
當你在(且僅在)抽象基類中使用 related_name 時,若是想繞過這個問題,就要在屬性值中包含 '%(class)s' 字符串。這個字符串會替換成字段所在子類的小寫名稱。由於每一個子類的命名都不一樣,因此 related_name 也會不同。例如:
class Base(models.Model): m2m = models.ManyToManyField(OtherModel, related_name="%(class)s_related") class Meta: abstract = True class ChildA(Base): pass class ChildB(Base): pass
ChildA.m2m 字段的反向名稱是 childa_related,而 ChildB.m2m 字段的反向名稱是 childb_related。這取決於你如何使用 '%(class)s' 來構造你的反向名稱。若是你沒有這樣作,Django 就會在驗證 model (或運行 syncdb) 時拋出錯誤。
若是你沒有在抽象基類中爲某個關聯字段定義 related_name 屬性,那麼默認的反向名稱就是子類名稱加上 '_set',它可否正常工做取決於你是否在子類中定義了同名字段。例如,在上面的代碼中,若是去掉 related_name 屬性,在 ChildA 中,m2m 字段的反向名稱就是 childa_set;而 ChildB 的 m2m 字段的反向名稱就是 childb_set 。
這是 Django 支持的第二種繼承方式。使用這種繼承方式時,同一層級下的每一個子 model 都是一個真正意義上完整的 model 。每一個子 model 都有專屬的數據表,均可以查詢和建立數據表。繼承關係在子 model 和它的每一個父類之間都添加一個連接 (經過一個自動建立的 OneToOneField 來實現)。 例如:
class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField()
Place 裏面的全部字段在 Restaurant 中也是有效的,只不過數據保存在另一張數據表當中。因此下面兩個語句都是能夠運行的:
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
若是你有一個 Place,那麼它同時也是一個 Restaurant, 那麼你可使用子 model 的小寫形式從 Place 對象中得到與其對應的 Restaurant 對象:
>>> p = Place.objects.filter(name="Bob's Cafe") # If Bob's Cafe is a Restaurant object, this will give the child class: >>> p.restaurant <Restaurant: ...>
可是,若是上例中的 p 並非 Restaurant (好比它僅僅只是 Place 對象,或者它是其餘類的父類),那麼在引用 p.restaurant 就會拋開Restaurant.DoesNotExist 異常。
在多表繼承中,子類繼承父類的 Meta 內嵌類是沒什麼意見的。全部的 Meta 選項已經對父類起了做用,再次使用只會起副作用。(這與使用抽象基類的狀況正好相反,由於抽象基類並無屬於它本身的內容)
因此子 model 並不能訪問它父類的 Meta 內嵌類。可是在某些受限的狀況下,子類能夠從父類繼承某些 Meta :若是子類沒有指定 django.db.models.Options.ordering 屬性或 django.db.models.Options.get_latest_by 屬性,它就會從父類中繼承這些屬性。
若是父類有了排序設置,而你並不想讓子類有任何排序設置,你就能夠顯式地禁用排序:
class ChildModel(ParentModel): ... class Meta: # Remove parent's ordering effect ordering = []
由於多表繼承使用了一個隱含的 OneToOneField 來連接子類與父類,因此象上例那樣,你能夠用父類來指代子類。可是這個 OnetoOneField 字段默認的 related_name 值與 django.db.models.fields.ForeignKey 和 django.db.models.fields.ManyToManyField 默認的反向名稱相同。若是你與其餘 model 的子類作多對一或是多對多關係,你就必須在每一個多對一和多對多字段上強制指定 related_name 。若是你沒這麼作,Django 就會在你運行 驗證(validate) 或 同步數據庫(syncdb) 時拋出異常。
例如,仍以上面 Place 類爲例,咱們建立一個帶有 ManyToManyField 字段的子類:
class Supplier(Place): # Must specify related_name on all relations. customers = models.ManyToManyField(Restaurant, related_name='provider')
以前咱們提到,Django 會自動建立一個 OneToOneField 字段將子類連接至非抽象的父 model 。若是你想指定連接父類的屬性名稱,你能夠建立你本身的 OneToOneField 字段並設置 parent_link=True ,從而使用該字段連接父類。
這部分是在 Django 1.1 中新增的: 請查看版本文檔
使用 多表繼承(multi-table inheritance) 時,model 的每一個子類都會建立一張新數據表,一般狀況下,這正是咱們想要的操做。這是由於子類須要一個空間來存儲不包含在基類中的字段數據。但有時,你可能只想更改 model 在 Python 層的行爲實現。好比:更改默認的 manager ,或是添加一個新方法。
而這,正是代理 model 繼承方式要作的:爲原始 model 建立一個代理(proxy)。你能夠建立,刪除,更新代理 model 的實例,並且全部的數據均可以象使用原始 model 同樣被保存。不一樣之處在於:你能夠在代理 model 中改變默認的排序設置和默認的 manager ,更不會對原始 model 產生影響。
聲明代理 model 和聲明普通 model 沒有什麼不一樣。設置Meta 內置類中 proxy 的值爲 True,就完成了對代理 model 的聲明。
舉個例子,假設你想給 Django 自帶的標準 User model (它被用在你的模板中)添加一個方法:
from django.contrib.auth.models import User class MyUser(User): class Meta: proxy = True def do_something(self): ...
MyUser 類和它的父類 User 操做同一個數據表。特別的是,User 的任何實例也能夠經過 MyUser 訪問,反之亦然:
>>> u = User.objects.create(username="foobar") >>> MyUser.objects.get(username="foobar") <MyUser: foobar>
你也可使用代理 model 給 model 定義不一樣的默認排序設置。Django 自帶的 User model 沒有定義排序設置(這是故意爲之,是由於排序開銷極大,咱們不想在獲取用戶時浪費額外資源)。你能夠利用代理對 username 屬性進行排序,這很簡單:
class OrderedUser(User): class Meta: ordering = ["username"] proxy = True
普通的 User 查詢,其結果是無序的;而 OrderedUser 查詢的結果是按 username 排序。
查詢集只返回請求時所使用的 model (Querysets still return the model that was requested)
不管你什麼時候查詢 User 對象,Django 都不會返回 MyUser 對象。針對 User 對象的查詢集只返回 User 對象。代理對象的精要就在於依賴原始 User 的代碼僅對它本身有效,而你本身的代碼就使用你擴展的內容。無論你怎麼改動,都不會在查詢 User 時獲得 MyUser。
代理 model 必須繼承自一個非抽象基類。你不能繼承自多個非抽象基類,這是由於一個代理 model 不能鏈接不一樣的數據表。代理 model 也能夠繼承任意多個抽象基類,但前提是它們沒有定義任何 model 字段。
代理 model 從非抽象基類中繼承那些未在代理 model 定義的 Meta 選項。
若是你沒有在代理 model 中定義任何 manager ,代理 model 就會從父類中繼承 manager 。若是你在代理 model 中定義了一個 manager ,它就會變成默認的 manager ,不過定義在父類中的 manager 還是有效的。
繼續上面的例子,你能夠改變默認 manager,例如:
class NewManager(models.Manager): ... class MyUser(User): objects = NewManager() class Meta: proxy = True
若是你想給代理添加一個新的 manager ,卻不想替換已有的默認 manager ,那麼你能夠參考 自定義 manager (custom manager) 中提到的方法:建立一個包含新 manager 的基類,而後放在主基類後面繼承:
# Create an abstract class for the new manager. class ExtraManagers(models.Model): secondary = NewManager() class Meta: abstract = True class MyUser(User, ExtraManagers): class Meta: proxy = True
你可能不須要常常這樣作,但這樣作是可行的。
代理 model 繼承看上去和使用 Meta 內嵌類中的 managed 屬性的非託管 model 很是類似。但二者並不相同,你應當考慮選用哪一種方案。
一個不一樣之處是你能夠在 Meta.managed=False 的 model 中定義字段(事實上,是必須指定,除非你真的想獲得一個空 model )。在建立非託管 model 時要謹慎設置 Meta.db_table ,這是由於建立的非託管 model 映射某個已存在的 model ,而且有本身的方法。所以,若是你要保證這兩個 model 同步並對程序進行改動,那麼就會變得繁冗而脆弱。
另外一個不一樣之處是二者對 manager 的處理方式不一樣。這對於代理 model 很是重要。代理 model 要與它所代理的 model 行爲類似,因此代理 model 要繼承父 model 的 managers ,包括它的默認 manager 。但在普通的多表繼承中,子類不能繼承父類的 manager ,這是由於在處理非基類字段時,父類的 manager 未必適用。在 manager documentation 有詳細介紹。
咱們實現了這兩種特性(Meta.proxy和Meta.unmanaged)以後,曾嘗試把二者結合到一塊兒。結果證實,宏觀的繼承關係和微觀的 manager 揉在一塊兒,不只致使 API 複雜難用,並且還難以理解。因爲任何場合下均可能須要這兩個選項,因此目前兩者還是各自獨立使用的。
因此,通常規則是:
若是你要鏡像一個已有的 model 或數據表,且不想涉及全部的原始數據表的列,那就令 Meta.managed=False。一般狀況下,對數據庫視圖建立 model 或是數據表不須要由 Django 控制時,就使用這個選項。
若是你想對 model 作 Python 層級的改動,又想保留字段不變,那就令 Meta.proxy=True。所以在數據保存時,代理 model 至關於徹底複製了原始 model 的存儲結構。
多重繼承(Multiple inheritance)
和 Python 同樣,Django 的 model 也能夠作多重繼承。這裏要記住 Python 的名稱解析規則。若是某個特定名稱 (例如,Meta) 出如今第一個基類當中,那麼子類就會使用第一個基類的該特定名稱。例如,若是多重父類都包含 Meta 內嵌類,只有第一個基類的 Meta 纔會被使用,其餘的都被會忽略。
通常來講,你不必使用多重繼承。多重繼承常見是用在 "mix-in" (對Mixin不瞭解的,請參閱賴勇浩的文章http://blog.csdn.net/lanphaday/archive/2007/06/18/1656969.aspx):給繼承自 mix-in的每一個類添加某個特定的字段或方法。儘量讓繼承結構簡單直接,這樣你就沒必要關注特定信息的來源。(注:這是說你沒必要花精力去窮究某個字段,屬性,方法是從哪一個父類繼承的)
普通的 Python 類繼承容許子類覆蓋父類的任何屬性。但在 Django 中,重寫 Field 實例是不容許的(至少如今還不行)。若是基類中有一個 author 字段,你就不能在子類中建立任何名爲 author 的字段。
重寫父類的字段會致使不少麻煩,好比:初始化實例(指定在 Model.__init__ 中被實例化的字段) 和序列化。而普通的 Python 類繼承機制並不能處理好這些特性。因此 Django 的繼承機制被設計成與 Python 有所不一樣,這樣作並非隨意而爲的。
這些限制僅僅針對作爲屬性使用的 Field 實例,並非針對 Python 屬性,Python 屬性還是能夠被重寫的。在 Python 看來,上面的限制僅僅針對字段實例的名稱:若是你手動指定了數據庫的列名稱,那麼在多重繼承中,你就能夠在子類和某個祖先類當中使用同一個列名稱。(由於它們使用的是兩個不一樣數據表的字段)。
若是你在任何一個祖先類中重寫了某個 model 字段,Django 都會拋出 FieldError 異常。