# 模型
模型是你的數據的唯一的、權威的信息源。它包含你所儲存數據的必要字段和行為。通常,每個模型對應數據庫中唯一的一張表。
基礎:
* 每個模型都是[`django.db.models.Model`](../../ref/models/instances.html#django.db.models.Model "django.db.models.Model") 的一個Python 子類。
* 模型的每個屬性都表示數據庫中的一個字段。
* Django 提供一套自動生成的用于數據庫訪問的API;詳見[_執行查詢_](queries.html)。
## 簡短的例子
這個例子定義一個`Person`模型,它有`first_name` 和`last_name` 兩個屬性:
```
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`是模型的兩個[字段](#fields)。每個字段都被指定成一個類屬性,每個屬性映射到一個數據庫的列。
上面的`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_](../../ref/models/options.html#table-names)。
* `id` 字段是自動添加的,但這個行為可以被重寫。詳見[_自增主鍵字段_](#automatic-primary-key-fields)。
* 這個例子中的`CREATE TABLE` SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據[_設置文件_](../settings.html) 中指定的數據庫類型來使用相應的SQL 語句。
## 使用模型
定義好模型之后,你需要告訴Django _使用_這些模型。你要做的就是修改配置文件中的[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS) 設置,在其中添加`models.py`所在應用的名稱。
例如,如果你的應用的模型位于`myapp.models`模塊([`manage.py startapp`](../../ref/django-admin.html#django-admin-startapp) 腳本為一個應用創建的包結構),[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS)部分看上去應該是:
```
INSTALLED_APPS = (
#...
'myapp',
#...
)
```
當你在[`INSTALLED_APPS`](../../ref/settings.html#std:setting-INSTALLED_APPS) 中添加新的應用名時,請確保運行命令[`manage.py migrate`](../../ref/django-admin.html#django-admin-migrate),可以首先使用[`manage.py makemigrations`](../../ref/django-admin.html#django-admin-makemigrations) 來為它們生成遷移腳本。
## 字段
模型中不可或缺且最為重要的,就是字段集,它是一組數據庫字段的列表。字段被指定為類屬性。 要注意選擇的字段名稱不要和[_模型 API_](../../ref/models/instances.html) 沖突,比如`clean`、`save` 或者`delete`。
例如:
```
from django.db import models
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()
```
### 字段類型
模型中的每個字段都是 [`Field`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field") 子類的某個實例。Django 根據字段類的類型確定以下信息:
* 數據庫當中的列類型 (比如, `INTEGER`, `VARCHAR`)。
* 渲染表單時使用的默認HTML [_部件_](../../ref/forms/widgets.html)(例如,`<input type="text">`, `<select>`)。
* 最低限度的驗證需求,它被用在 Django 管理站點和自動生成的表單中。
Django 自帶數十種內置的字段類型;完整字段類型列表可以在[_模型字段參考_](../../ref/models/fields.html#model-field-types) 中找到。如果內置類型仍不能滿足你的要求,你可以自由地編寫符合你要求的字段類型; 詳見[_編寫自定義的模型字段_](../../howto/custom-model-fields.html)。
### 字段選項
每個字段有一些特有的參數,詳見[_模型字段參考_](../../ref/models/fields.html#model-field-types)。例如,[`CharField`](../../ref/models/fields.html#django.db.models.CharField "django.db.models.CharField")(和它的派生類)需要[`max_length`](../../ref/models/fields.html#django.db.models.CharField.max_length "django.db.models.CharField.max_length") 參數來指定`VARCHAR` 數據庫字段的大小。
還有一些適用于所有字段的通用參數。 這些參數在[_參考_](../../ref/models/fields.html#common-model-field-options)中有詳細定義,這里我們只簡單介紹一些最常用的:
[`null`](../../ref/models/fields.html#django.db.models.Field.null "django.db.models.Field.null")
如果為`True`,Django 將用`NULL` 來在數據庫中存儲空值。 默認值是 `False`.
[`blank`](../../ref/models/fields.html#django.db.models.Field.blank "django.db.models.Field.blank")
如果為`True`,該字段允許不填。默認為`False`。
要注意,這與 [`null`](../../ref/models/fields.html#django.db.models.Field.null "django.db.models.Field.null") 不同。[`null`](../../ref/models/fields.html#django.db.models.Field.null "django.db.models.Field.null")純粹是數據庫范疇的,而 [`blank`](../../ref/models/fields.html#django.db.models.Field.blank "django.db.models.Field.blank") 是數據驗證范疇的。如果一個字段的[`blank=True`](../../ref/models/fields.html#django.db.models.Field.blank "django.db.models.Field.blank"),表單的驗證將允許該字段是空值。如果字段的[`blank=False`](../../ref/models/fields.html#django.db.models.Field.blank "django.db.models.Field.blank"),該字段就是必填的。
[`choices`](../../ref/models/fields.html#django.db.models.Field.choices "django.db.models.Field.choices")
由二元組組成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 如果設置了choices ,默認的表單將是一個選擇框而不是標準的文本框,而且這個選擇框的選項就是choices 中的選項。
這是一個關于 choices 列表的例子:
```
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
```
每個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是在管理界面或 ModelChoiceField 中用作顯示的內容。 在一個給定的 model 類的實例中,想得到某個 choices 字段的顯示值,就調用 `get_FOO_display` 方法(這里的 FOO 就是 choices 字段的名稱 )。例如:
```
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=1, choices=SHIRT_SIZES)
```
```
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
```
[`default`](../../ref/models/fields.html#django.db.models.Field.default "django.db.models.Field.default")
字段的默認值。可以是一個值或者可調用對象。如果可調用 ,每有新對象被創建它都會被調用。
[`help_text`](../../ref/models/fields.html#django.db.models.Field.help_text "django.db.models.Field.help_text")
表單部件額外顯示的幫助內容。即使字段不在表單中使用,它對生成文檔也很有用。
[`primary_key`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key")
如果為`True`,那么這個字段就是模型的主鍵。
如果你沒有指定任何一個字段的[`primary_key=True`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key"),Django 就會自動添加一個[`IntegerField`](../../ref/models/fields.html#django.db.models.IntegerField "django.db.models.IntegerField") 字段做為主鍵,所以除非你想覆蓋默認的主鍵行為,否則沒必要設置任何一個字段的[`primary_key=True`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key")。詳見[_自增主鍵字段_](#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`](../../ref/models/fields.html#django.db.models.Field.unique "django.db.models.Field.unique")
如果該值設置為 `True`, 這個數據字段的值在整張表中必須是唯一的
再說一次,這些僅僅是常用字段的簡短介紹, 要了解詳細內容,請查看 通用 model 字段選項參考([_common model field option reference_](../../ref/models/fields.html#common-model-field-options)).
### 自增主鍵字段
默認情況下,Django 會給每個模型添加下面這個字段:
```
id = models.AutoField(primary_key=True)
```
這是一個自增主鍵字段。
如果你想指定一個自定義主鍵字段,只要在某個字段上指定 [`primary_key=True`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key") 即可。如果 Django 看到你顯式地設置了 [`Field.primary_key`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key"),就不會自動添加 `id` 列。
每個模型只能有一個字段指定[`primary_key=True`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key")(無論是顯式聲明還是自動添加)。
### 字段的自述名
除[`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey")、[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 和 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.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`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey")、[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 和 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField") 都要求第一個參數是一個模型類,所以要使用 [`verbose_name`](../../ref/models/fields.html#django.db.models.Field.verbose_name "django.db.models.Field.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`](../../ref/models/fields.html#django.db.models.Field.verbose_name "django.db.models.Field.verbose_name") 的首字母不用大寫。Django 在必要的時候會自動大寫首字母。
### 關系
顯然,關系數據庫的威力體現在表之間的相互關聯。 Django 提供了三種最常見的數據庫關系:多對一(many-to-one),多對多(many-to-many),一對一(one-to-one)。
#### 多對一關系
Django 使用 [`django.db.models.ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 定義多對一關系。和使用其它[`字段`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field")類型一樣:在模型當中把它做為一個類屬性包含進來。
[`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 需要一個位置參數:與該模型關聯的類。
比如,一輛`Car`有一個`Manufacturer` —— 但是一個`Manufacturer` 生產很多Car,每一輛`Car` 只能有一個`Manufacturer` —— 使用下面的定義:
```
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
# ...
```
你還可以創建[_遞歸的關聯關系_](../../ref/models/fields.html#recursive-relationships)(對象和自己進行多對一關聯)和 [_與尚未定義的模型的關聯關系_](../../ref/models/fields.html#lazy-relationships);詳見[_模型字段參考_](../../ref/models/fields.html#ref-foreignkey)。
建議你用被關聯的模型的小寫名稱做為[`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 字段的名字(例如,上面`manufacturer`)。當然,你也可以起別的名字。例如:
```
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
# ...
```
另見
[`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 字段還接受許多別的參數,在[_模型字段參考_](../../ref/models/fields.html#foreign-key-arguments)有詳細介紹。這些選項幫助定義關聯關系應該如何工作;它們都是可選的參數。
訪問反向關聯對象的細節,請見[_Following relationships backward example_](queries.html#backwards-related-objects)。
示例代碼,請見[_多對一關系模型示例_](examples/many_to_one.html))。
#### 多對多關系
[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 用來定義多對多關系,用法和其他[`Field`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field") 字段類型一樣:在模型中做為一個類屬性包含進來。
[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 需要一個位置參數:和該模型關聯的類。
例如,一個`Pizza`可以有多種`Topping` —— 一種`Topping` 可以位于多個Pizza 上,而且每個`Pizza` 可以有多種Topping —— 如下:
```
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
```
和使用[`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 一樣,你也可以創建[_遞歸的關聯關系_](../../ref/models/fields.html#recursive-relationships)(對象與自己的多對多關聯)和[_與尚未定義關系的模型的關聯關系_](../../ref/models/fields.html#lazy-relationships);詳見[_模型字段參考_](../../ref/models/fields.html#ref-manytomany)。
建議你以被關聯模型名稱的復數形式做為[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 的名字(例如上例中的`toppings`)。
在哪個模型中設置 [`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 并不重要,在兩個模型中任選一個即可 —— 不要兩個模型都設置。
通常,[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 實例應該位于可以編輯的表單中。在上面的例子中,`toppings` 位于`Pizza` 中(而不是在 `Topping` 里面設置`pizzas` 的 [`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 字段),因為設想一個Pizza 有多種Topping 比一個Topping 位于多個Pizza 上要更加自然。按照上面的方式,在`Pizza` 的表單中將允許用戶選擇不同的Toppings。
另見
完整的示例參見[_多對多關系模型示例_](examples/many_to_many.html)。
[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 字段還接受別的參數,在[_模型字段參考_](../../ref/models/fields.html#manytomany-arguments)中有詳細介紹。這些選項幫助定義關系應該如何工作;它們都是可選的。
#### 多對多關系中的其他字段
處理類似搭配 pizza 和 topping 這樣簡單的多對多關系時,使用標準的[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 就可以了。但是,有時你可能需要關聯數據到兩個模型之間的關系上。
例如,有這樣一個應用,它記錄音樂家所屬的音樂小組。我們可以用一個[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 表示小組和成員之間的多對多關系。但是,有時你可能想知道更多成員關系的細節,比如成員是何時加入小組的。
對于這些情況,Django 允許你指定一個模型來定義多對多關系。 你可以將其他字段放在中介模型里面。源模型的[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 字段將使用[`through`](../../ref/models/fields.html#django.db.models.ManyToManyField.through "django.db.models.ManyToManyField.through") 參數指向中介模型。對于上面的音樂小組的例子,代碼如下:
```
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
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)
```
在設置中介模型時,要顯式指定外鍵并關聯到多對多關系涉及的模型。這個顯式聲明定義兩個模型之間是如何關聯的。
中介模型有一些限制:
* 中介模型必須_有且只有一個_外鍵到源模型(上面例子中的`Group`),或者你必須使用[`ManyToManyField.through_fields`](../../ref/models/fields.html#django.db.models.ManyToManyField.through_fields "django.db.models.ManyToManyField.through_fields") 顯式指定Django 應該使用的外鍵。如果你的模型中存在超個一個的外鍵,并且`through_fields`沒有指定,將會觸發一個無效的錯誤。 對目標模型的外鍵有相同的限制(上面例子中的 `Person`)。
* 對于通過中介模型與自己進行多對多關聯的模型,允許存在到同一個模型的兩個外鍵,但它們將被作為多對多關聯關系的兩個(不同的)方面。如果有_超過_ 兩個外鍵,同樣你必須像上面一樣指定`through_fields`,否則將引發一個驗證錯誤。
* 使用中介模型定義與自身的多對多關系時,你_必須_設置 [`symmetrical=False`](../../ref/models/fields.html#django.db.models.ManyToManyField.symmetrical "django.db.models.ManyToManyField.symmetrical")(詳見[_模型字段參考_](../../ref/models/fields.html#manytomany-arguments))。
Changed in Django 1.7:
在Django 1.6 及之前的版本中,中介模型禁止包含多于一個的外鍵。
既然你已經設置好[`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 來使用中介模型(在這個例子中就是`Membership`),接下來你要開始創建多對多關系。你要做的就是創建中介模型的實例:
```
>>> 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`和 `Group`之間的關聯關系,你還要指定 `Membership`模型中所需要的所有信息;而簡單的`add`、`create` 和賦值語句是做不到這一點的。所以它們不能在使用中介模型的多對多關系中使用。此時,唯一的辦法就是創建中介模型的實例。
[`remove()`](../../ref/models/relations.html#django.db.models.fields.related.RelatedManager.remove "django.db.models.fields.related.RelatedManager.remove")方法被禁用也是出于同樣的原因。但是[`clear()`](../../ref/models/relations.html#django.db.models.fields.related.RelatedManager.clear "django.db.models.fields.related.RelatedManager.clear") 方法卻是可用的。它可以清空某個實例所有的多對多關系:
```
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
[]
```
通過創建中介模型的實例來建立對多對多關系后,你就可以執行查詢了。 和普通的多對多字段一樣,你可以直接使用被關聯模型的屬性進行查詢:
```
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]
```
如果你使用了中介模型,你也可以利用中介模型的屬性進行查詢:
```
# 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]
```
如果你需要訪問一個成員的信息,你可以直接獲取`Membership`模型:
```
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
```
另一種獲取相同信息的方法是,在`Person`對象上查詢[_多對多反轉關系_](queries.html#m2m-reverse-relationships):
```
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
```
#### 一對一關系
[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")用來定義一對一關系。 用法和其他`字段`類型一樣:在模型里面做為類屬性包含進來。
當某個對象想擴展自另一個對象時,最常用的方式就是在這個對象的主鍵上添加一對一關系。
[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")要一個位置參數:與模型關聯的類。
例如,你想建一個“places” 數據庫,里面有一些常用的字段,比如address、 phone number 等等。 接下來,如果你想在Place 數據庫的基礎上建立一個Restaurant 數據庫,而不想將已有的字段復制到`Restaurant`模型,那你可以在 `Restaurant` 添加一個[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField") 字段,這個字段指向`Place`(因為Restaurant 本身就是一個Place;事實上,在處理這個問題的時候,你應該使用一個典型的 [_繼承_](#model-inheritance),它隱含一個一對一關系)。
和使用 [`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey")一樣,你可以定義[_ 遞歸的關聯關系_](../../ref/models/fields.html#recursive-relationships)和[_引用尚未定義關系的模型_](../../ref/models/fields.html#lazy-relationships)。詳見[_模型字段參考_](../../ref/models/fields.html#ref-onetoone)。
另見
在[_一對一關系的模型例子_](examples/one_to_one.html) 中有一套完整的例子。
[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")字段也接受一個特定的可選的`parent_link`參數,在[_模型字段參考_](../../ref/models/fields.html#ref-onetoone) 中有詳細介紹。
在以前的版本中,[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField") 字段會自動變成模型 的主鍵。不過現在已經不這么做了(不過要是你愿意的話,你仍可以傳遞 [`primary_key`](../../ref/models/fields.html#django.db.models.Field.primary_key "django.db.models.Field.primary_key")參數來創建主鍵字段)。所以一個 模型 中可以有多個[`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField") 字段。
### 跨文件的模型
訪問其他應用的模型是非常容易的。 在文件頂部你定義模型的地方,導入相關的模型來實現它。然后,無論在哪里需要的話,都可以引用它。例如:
```
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
```
### 字段命名的限制
Django 對字段的命名只有兩個限制:
1. 字段的名稱不能是Python 保留的關鍵字,因為這將導致一個Python 語法錯誤。例如:
```
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
```
2. 由于Django 查詢語法的工作方式,字段名稱中連續的下劃線不能超過一個。例如:
```
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
```
這些限制有變通的方法,因為沒有要求字段名稱必須與數據庫的列名匹配。參 [`db_column`](../../ref/models/fields.html#django.db.models.Field.db_column "django.db.models.Field.db_column") 選項。
SQL 的保留字例如`join`、`where` 和`select`,可以用作模型的字段名,因為Django 會對底層的SQL 查詢語句中的數據庫表名和列名進行轉義。 它根據你的數據庫引擎使用不同的引用語法。
### 自定義字段類型
如果已有的模型字段都不合適,或者你想用到一些很少見的數據庫列類型的優點,你可以創建你自己的字段類型。創建你自己的字段在[_編寫自定義的模型字段_](../../howto/custom-model-fields.html)中有完整講述。
## 元選項
使用內部的`class Meta` 定義模型的元數據,例如:
```
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
```
模型元數據是“任何不是字段的數據”,比如排序選項([`ordering`](../../ref/models/options.html#django.db.models.Options.ordering "django.db.models.Options.ordering")),數據表名([`db_table`](../../ref/models/options.html#django.db.models.Options.db_table "django.db.models.Options.db_table"))或者人類可讀的單復數名稱([`verbose_name`](../../ref/models/options.html#django.db.models.Options.verbose_name "django.db.models.Options.verbose_name") 和[`verbose_name_plural`](../../ref/models/options.html#django.db.models.Options.verbose_name_plural "django.db.models.Options.verbose_name_plural"))。在模型中添加`class Meta`是完全可選的,所有選項都不是必須的。
所有`元`選項的完整列表可以在[_模型選項參考_](../../ref/models/options.html)找到。
## 模型的屬性
`objects`
The most important attribute of a model is the
[`Manager`](managers.html#django.db.models.Manager "django.db.models.Manager"). It’s the interface through which
database query operations are provided to Django models and is used to
[_retrieve the instances_](queries.html#retrieving-objects) from the database. If no
custom `Manager` is defined, the default name is
[`objects`](../../ref/models/class.html#django.db.models.Model.objects "django.db.models.Model.objects"). Managers are only accessible via
model classes, not the model instances.
## 模型的方法
可以在模型上定義自定義的方法來給你的對象添加自定義的“底層”功能。[`Manager`](managers.html#django.db.models.Manager "django.db.models.Manager") 方法用于“表范圍”的事務,模型的方法應該著眼于特定的模型實例。
這是一個非常有價值的技術,讓業務邏輯位于同一個地方 —— 模型中。
例如,下面的模型具有一些自定義的方法:
```
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
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_](../../glossary.html#term-property)。
[_模型實例參考_](../../ref/models/instances.html) 具有一個完整的[_為模型自動生成的方法_](../../ref/models/instances.html#model-instance-methods) 列表。你可以覆蓋它們 —— 參見下文[覆蓋模型預定義的方法](#overriding-predefined-model-methods) —— 但是有些方法你會始終想要重新定義:
[`__str__()`](../../ref/models/instances.html#django.db.models.Model.__str__ "django.db.models.Model.__str__") (Python 3)
Python 3 equivalent of `__unicode__()`.
[`__unicode__()`](../../ref/models/instances.html#django.db.models.Model.__unicode__ "django.db.models.Model.__unicode__") (Python 2)
一個Python “魔法方法”,返回對象的Unicode “表示形式”。當模型實例需要強制轉換并顯示為普通的字符串時,Python 和Django 將使用這個方法。最明顯是在交互式控制臺或者管理站點顯示一個對象的時候。
將將永遠想要定義這個方法;默認的方法幾乎沒有意義。
[`get_absolute_url()`](../../ref/models/instances.html#django.db.models.Model.get_absolute_url "django.db.models.Model.get_absolute_url")
它告訴Django 如何計算一個對象的URL。Django 在它的管理站點中使用到這個方法,在其它任何需要計算一個對象的URL 時也將用到。
任何具有唯一標識自己的URL 的對象都應該定義這個方法。
### 覆蓋預定義的模型方法
還有另外一部分封裝數據庫行為的[_模型方法_](../../ref/models/instances.html#model-instance-methods),你可能想要自定義它們。特別是,你將要經常改變[`save()`](../../ref/models/instances.html#django.db.models.Model.save "django.db.models.Model.save") 和[`delete()`](../../ref/models/instances.html#django.db.models.Model.delete "django.db.models.Model.delete") 的工作方式。
你可以自由覆蓋這些方法(和其它任何模型方法)來改變它們的行為。
覆蓋內建模型方法的一個典型的使用場景是,你想在保存一個對象時做一些其它事情。例如(參見[`save()`](../../ref/models/instances.html#django.db.models.Model.save "django.db.models.Model.save") 中關于它接受的參數的文檔):
```
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
```
你還可以阻止保存:
```
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
```
必須要記住調用超類的方法—— `super(Blog, self).save(*args, **kwargs)` —— 來確保對象被保存到數據庫中。如果你忘記調用超類的這個方法,默認的行為將不會發生且數據庫不會有任何改變。
還要記住傳遞參數給這個模型方法 —— 即`*args, **kwargs`。 Django 未來將一直會擴展內建模型方法的功能并添加新的參數。如果在你的方法定義中使用`*args, **kwargs`,將保證你的代碼自動支持這些新的參數。
Overridden model methods are not called on bulk operations
注意,當[_使用查詢集批量刪除對象時_](queries.html#topics-db-queries-delete),將不會為每個對象調用[`delete()`](../../ref/models/instances.html#django.db.models.Model.delete "django.db.models.Model.delete") 方法。為確保自定義的刪除邏輯得到執行,你可以使用[`pre_delete`](../../ref/signals.html#django.db.models.signals.pre_delete "django.db.models.signals.pre_delete") 和/或[`post_delete`](../../ref/signals.html#django.db.models.signals.post_delete "django.db.models.signals.post_delete") 信號。
不幸的是,當批量[`creating`](../../ref/models/querysets.html#django.db.models.query.QuerySet.bulk_create "django.db.models.query.QuerySet.bulk_create") 或[`updating`](../../ref/models/querysets.html#django.db.models.query.QuerySet.update "django.db.models.query.QuerySet.update") 對象時沒有變通方法,因為不會調用[`save()`](../../ref/models/instances.html#django.db.models.Model.save "django.db.models.Model.save")、[`pre_save`](../../ref/signals.html#django.db.models.signals.pre_save "django.db.models.signals.pre_save")和 [`post_save`](../../ref/signals.html#django.db.models.signals.post_save "django.db.models.signals.post_save")。
### 執行自定義的SQL
另外一個常見的需求是在模型方法和模塊級別的方法中編寫自定義的SQL 語句。關于使用原始SQL 語句的更多細節,參見[_使用原始 SQL_](sql.html) 的文檔。
## 模型繼承
Django 中的模型繼承與 Python 中普通類繼承方式幾乎完全相同,但是本頁頭部列出的模型基本的要求還是要遵守。這表示自定義的模型類應該繼承[`django.db.models.Model`](../../ref/models/instances.html#django.db.models.Model "django.db.models.Model")。
你唯一需要作出的決定就是你是想讓父模型具有它們自己的數據庫表,還是讓父模型只持有一些共同的信息而這些信息只有在子模型中才能看到。
在Django 中有3中風格的繼承。
1. 通常,你只想使用父類來持有一些信息,你不想在每個子模型中都敲一遍。這個類永遠不會單獨使用,所以你使用[_抽象基類_](#abstract-base-classes)。
2. 如果你繼承一個已經存在的模型且想讓每個模型具有它自己的數據庫表,那么應該使用[_多表繼承_](#multi-table-inheritance)。
3. 最后,如果你只是想改變模塊Python 級別的行為,而不用修改模型的字段,你可以使用[_代理模型_](#proxy-models)。
### 抽象基類
當你想將一些常見信息存儲到很多model的時候,抽象化類是十分有用的。你編寫完基類之后,在 [_Meta_](#meta-options)類中設置 `abstract=True` ,該類就不能創建任何數據表。取而代之的是,當它被用來作為一個其他model的基礎類時,它將被加入那一子類中。如果抽象化基礎類和它的子類有相同的項,那么將會出現error(并且Django將返回一個exception)。
一個例子
```
from django.db import models
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` 模型將有三個項:`name`, `age` 和 `home_group`。`CommonInfo` 模型無法像一般的Django模型一樣使用,因為它是一個抽象化基礎類。它無法生成數據表單或者管理器,并且不能實例化或者儲存。
對很多用戶來說, 這種類型的模型繼承就是你想要的。它提供一種在 Python 語言層級上提取公共信息的方式,但在數據庫層級上,各個子類仍然只創建一個數據庫。
#### `元` 繼承
當一個抽象類被創建的時候, Django會自動把你在基類中定義的 [_Meta_](#meta-options) 作為子類的一個屬性。如果子類沒有聲明自己的[_Meta_](#meta-options) 類, 他將會繼承父類的[_Meta_](#meta-options). 如果子類想要擴展父類 的,可以繼承父類的 [_Meta_](#meta-options) 即可,例如
```
from django.db import models
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
```
繼承時,Django 會對基類的 [_Meta_](#meta-options)類做一個調整:在安裝 [_Meta_](#meta-options)屬性之前,Django 會設置 `abstract=False`。這意味著抽象基類的子類不會自動變成抽象類。 當然,你可以讓一個抽象類繼承另一個抽象基類,不過每次都要顯式地設置 `abstract=True`。
對于抽象基類而言,有些屬性放在 [_Meta_](#meta-options) 內嵌類里面是沒有意義的。例如,包含 `db_table`將意味著所有的子類(是指那些沒有指定自己的 [_Meta_](#meta-options) 類的子類)都使用同一張數據表,一般來說,這并不是我們想要的。
#### 小心使用 `related_name`
如果你在 `ForeignKey`或 `ManyToManyField`字段上使用 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name")屬性,你必須總是為該字段指定一個_唯一_的反向名稱。但在抽象基類上這樣做就會引發一個很嚴重的問題。因為 Django 會將基類字段添加到每個子類當中,而每個子類的字段屬性值都完全相同 (這里面就包括[`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name"))。
當你在(且僅在)抽象基類中使用 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name") 時,如果想繞過這個問題,名稱中就要包含`'%(app_label)s'`和 `'%(class)s'`。
* `'%(class)s'` 會替換為子類的小寫加下劃線格式的名稱,字段在子類中使用。
* `'%(app_label)s'` 會替換為應用的小寫加下劃線格式的名稱,應用包含子類。每個安裝的應用名稱都應該是唯一的,而且應用里每個模型類的名稱也應該是唯一的,所以產生的名稱應該彼此不同。
例如,假設有一個app叫做`common/models.py`:
```
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
class Meta:
abstract = True
class ChildA(Base):
pass
class ChildB(Base):
pass
```
以及另一個應用 `rare/models.py`:
```
from common.models import Base
class ChildB(Base):
pass
```
ChildA.m2m 字段的反向名稱是 childa_related,而 ChildB.m2m 字段的反向名稱是 childb_related。這取決于你如何使用 `'%(class)s'` 和`'%(app_label)s`來構造你的反向名稱。如果你沒有這樣做,Django 就會在驗證 model (或運行 [`migrate`](../../ref/django-admin.html#django-admin-migrate)) 時拋出錯誤。
果你沒有在抽象基類中為某個關聯字段定義 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name") 屬性,那么默認的反向名稱就是子類名稱加上`'_set'`,它能否正常工作取決于你是否在子類中定義了同名字段。例如,在上面的代碼中,如果去掉 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name")屬性,在 `ChildA`中,`m2m` 字段的反向名稱就是 `childa_set`;而 `ChildB`的 m2m 字段的反向名稱就是 `childb_set`。
### 多表繼承
這是 Django 支持的第二種繼承方式。使用這種繼承方式時,同一層級下的每個子 model 都是一個真正意義上完整的 model 。 每個子 model 都有專屬的數據表,都可以查詢和創建數據表。 繼承關系在子 model 和它的每個父類之間都添加一個鏈接 (通過一個自動創建的 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")來實現)。 例如:
```
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
```
`Place`里面的所有字段在 `Restaurant`中也是有效的,只不過數據保存在另外一張數據表當中。所以下面兩個語句都是可以運行的:
```
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
```
如果你有一個 `Place` ,那么它同時也是一個 `Restaurant`, 那么你可以使用子 model 的小寫形式從 Pl`Place` 對象中獲得與其對應的 `Restaurant`對象:
```
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
```
但是,如果上例中的 `p` 并_不是_`Restaurant` (比如它僅僅只是 `Place`對象,或者它是其他類的父類),那么在引用 `p.restaurant`就會拋出`Restaurant.DoesNotExist` 異常。
#### 多表繼承中的`Meta`
在多表繼承中,子類繼承父類的 [_Meta_](#meta-options)類是沒什么意義的。所有的 [_Meta_](#meta-options) 選項已經對父類起了作用,再次使用只會起反作用。(這與使用抽象基類的情況正好相反,因為抽象基類并沒有屬于它自己的內容)
所以子 model 并不能訪問它父類的 [_Meta_](#meta-options) 類。但是在某些受限的情況下,子類可以從父類繼承某些 Meta :如果子類沒有指定 [`ordering`](../../ref/models/options.html#django.db.models.Options.ordering "django.db.models.Options.ordering")屬性或 [`get_latest_by`](../../ref/models/options.html#django.db.models.Options.get_latest_by "django.db.models.Options.get_latest_by") 屬性,它就會從父類中繼承這些屬性。
如果父類有了排序設置,而你并不想讓子類有任何排序設置,你就可以顯式地禁用排序:
```
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent's ordering effect
ordering = []
```
#### 繼承與反向關聯
因為多表繼承使用了一個隱含的 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")來鏈接子類與父類,所以象上例那樣,你可以用父類來指代子類。但是這個 OnetoOneField 字段默認的 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name") 值與 [`ForeignKey`](../../ref/models/fields.html#django.db.models.ForeignKey "django.db.models.ForeignKey") 和 [`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField") 默認的反向名稱相同。如果你與其他 model 的子類做多對一或是多對多關系,你就**必須**在每個多對一和多對多字段上強制指定 [`related_name`](../../ref/models/fields.html#django.db.models.ForeignKey.related_name "django.db.models.ForeignKey.related_name")。如果你沒這么做,Django 就會在你運行 驗證(validation) 時拋出異常。
例如,仍以上面 `Place`類為例,我們創建一個帶有 [`ManyToManyField`](../../ref/models/fields.html#django.db.models.ManyToManyField "django.db.models.ManyToManyField")字段的子類:
```
class Supplier(Place):
customers = models.ManyToManyField(Place)
```
這會產生一個錯誤:
```
Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.
HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
```
像下面那樣,向`customers`字段中添加`related_name`可以解決這個錯誤:`models.ManyToManyField(Place, related_name='provider')`。
#### 指定鏈接父類的字段
之前我們提到,Django 會自動創建一個 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")字段將子類鏈接至非抽象的父 model 。如果你想指定鏈接父類的屬性名稱,你可以創建你自己的 [`OneToOneField`](../../ref/models/fields.html#django.db.models.OneToOneField "django.db.models.OneToOneField")字段并設置 [`parent_link=True`](../../ref/models/fields.html#django.db.models.OneToOneField.parent_link "django.db.models.OneToOneField.parent_link") ,從而使用該字段鏈接父類。
### 代理模型
使用 [_多表繼承_](#multi-table-inheritance)時,model 的每個子類都會創建一張新數據表,通常情況下,這正是我們想要的操作。這是因為子類需要一個空間來存儲不包含在基類中的字段數據。 但有時,你可能只想更改 model 在 Python 層的行為實現。比如:更改默認的 manager ,或是添加一個新方法。
而這,正是代理 model 繼承方式要做的:為原始 model 創建一個_代理_ 。你可以創建,刪除,更新代理 model 的實例,而且所有的數據都可以象使用原始 model 一樣被保存。 不同之處在于:你可以在代理 model 中改變默認的排序設置和默認的 manager ,更不會對原始 model 產生影響。
聲明代理 model 和聲明普通 model 沒有什么不同。 設置`Meta`類中 [`proxy`](../../ref/models/options.html#django.db.models.Options.proxy "django.db.models.Options.proxy") 的值為 `True`,就完成了對代理 model 的聲明。
舉個例子,假設你想給 Django 自帶的標準 `Person` model添加一個方法。你可以這樣做:
```
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
class Meta:
proxy = True
def do_something(self):
# ...
pass
```
`MyPerson`類和它的父類 `Person` 操作同一個數據表。特別的是,`Person` 的任何實例也可以通過 `MyPerson`訪問,反之亦然:
```
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
```
你也可以使用代理 model 給 model 定義不同的默認排序設置。 你可能并不想每次都給`Person`模型排序,但是使用代理的時候總是按照`last_name`屬性排序。這非常容易:
```
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
```
現在,普通的`Person`查詢時無序的,而 `OrderedPerson`查詢會按照`last_name`排序。
#### 查詢集始終返回請求的模型
也就是說,沒有辦法讓DJango在查詢`Person`對象時返回`MyPerson`對象。`Person` 對象的查詢集會返回相同類型的對象。代理對象的要點是,依賴于原生`Person`對象的代碼仍然使用它,而你可以使用你添加進來的擴展對象(它不會依賴其它任何代碼)。而并不是將`Person`模型(或者其它)在所有地方替換為其它你自己創建的模型。
#### 基類的限制
代理 模型必須繼承自一個非抽象基類。 你不能繼承自多個非抽象基類,這是因為一個代理 model 不能連接不同的數據表。 代理 model 也可以繼承任意多個抽象基類,但前提是它們_沒有_ 定義任何 model 字段。
#### 代理模型的管理器
如果你沒有在代理 模型中定義任何 管理器 ,代理模型就會從父類中繼承 管理器 。 如果你在代理 模型中定義了一個 管理器 ,它就會變成默認的管理器 ,不過定義在父類中的管理器仍然有效。
繼續上面的例子,當你查詢`Person`模型的時候,你可以改變默認 管理器,例如:
```
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
```
如果你想要向代理中添加新的管理器,而不是替換現有的默認管理器,你可以使用[_自定義管理器_](managers.html#custom-managers-and-inheritance)管理器文檔中描述的技巧:創建一個含有新的管理器的基類,并且在主基類之后繼承它:
```
# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
secondary = NewManager()
class Meta:
abstract = True
class MyPerson(Person, ExtraManagers):
class Meta:
proxy = True
```
你可能不需要經常這樣做,但這樣做是可行的。
#### 代理 模型與非托管 模型之間的差異
代理 model 繼承看上去和使用`Meta`類中的 [`managed`](../../ref/models/options.html#django.db.models.Options.managed "django.db.models.Options.managed") 屬性的非托管 model 非常相似。但兩者并不相同,你應當考慮選用哪種方案。
一個不同之處是你可以在`Meta.managed=False`的 model 中定義字段(事實上,是必須指定,除非你真的想得到一個空 model )。在創建非托管 model 時要謹慎設置[`Meta.db_table`](../../ref/models/options.html#django.db.models.Options.db_table "django.db.models.Options.db_table") ,這是因為創建的非托管 model 映射某個已存在的 model ,并且有自己的方法。因此,如果你要保證這兩個 model 同步并對程序進行改動,那么就會變得繁冗而脆弱。
另一個不同之處是兩者對 管理器的處理方式不同。 代理 model 要與它所代理的 model 行為相似,所以代理 model 要繼承父 model 的 managers ,包括它的默認 manager 。 但在普通的多表繼承中,子類不能繼承父類的 manager ,這是因為在處理非基類字段時,父類的 manager 未必適用。 后一種情況在 [_管理器文檔_](managers.html#custom-managers-and-inheritance)有詳細介紹。
我們實現了這兩種特性之后,曾嘗試把兩者結合到一起。 結果證明,宏觀的繼承關系和微觀的 管理器揉在一起,不僅導致 API 復雜難用,而且還難以理解。 由于任何場合下都可能需要這兩個選項,所以目前二者仍是各自獨立使用的。
所以,一般規則是:
1. 如果你要借鑒一個已有的 模型或數據表,且不想涉及所有的原始數據表的列,那就令 `Meta.managed=False`。通常情況下,對數據庫視圖創建 模型或是數據表不需要由 Django 控制時,就使用這個選項。
2. 如果你想對 model 做 Python 層級的改動,又想保留字段不變,那就令 `Meta.proxy=True`。因此在數據保存時,代理 model 相當于完全復制了原始 模型的存儲結構。
### 多重繼承
就像Python的子類那樣,DJango的模型可以繼承自多個父類模型。切記一般的Python名稱解析規則也會適用。出現特定名稱的第一個基類(比如[_Meta_](#meta-options))是所使用的那個。例如,這意味著如果多個父類含有 [_Meta_](#meta-options)類,只有第一個會被使用,剩下的會忽略掉。
一般來說,你并不需要繼承多個父類。多重繼承主要對“mix-in”類有用:向每個繼承mix-in的類添加一個特定的、額外的字段或者方法。你應該嘗試將你的繼承關系保持得盡可能簡潔和直接,這樣你就不必費很大力氣來弄清楚某段特定的信息來自哪里。
Changed in Django 1.7\.
Django 1.7之前,繼承多個含有`id`主鍵字段的模型不會拋出異常,但是會導致數據丟失。例如,考慮這些模型(由于`id`字段的沖突,它們不再有效):
```
class Article(models.Model):
headline = models.CharField(max_length=50)
body = models.TextField()
class Book(models.Model):
title = models.CharField(max_length=50)
class BookReview(Book, Article):
pass
```
這段代碼展示了如何創建子類的對象,并覆寫之前創建的父類對象中的值。
```
>>> article = Article.objects.create(headline='Some piece of news.')
>>> review = BookReview.objects.create(
... headline='Review of Little Red Riding Hood.',
... title='Little Red Riding Hood')
>>>
>>> assert Article.objects.get(pk=article.pk).headline == article.headline
Traceback (most recent call last):
File "<console>", line 1, in <module>
AssertionError
>>> # the "Some piece of news." headline has been overwritten.
>>> Article.objects.get(pk=article.pk).headline
'Review of Little Red Riding Hood.'
```
你可以在模型基類中使用顯式的[`AutoField`](../../ref/models/fields.html#django.db.models.AutoField "django.db.models.AutoField")來合理使用多重繼承:
```
class Article(models.Model):
article_id = models.AutoField(primary_key=True)
...
class Book(models.Model):
book_id = models.AutoField(primary_key=True)
...
class BookReview(Book, Article):
pass
```
或者是使用一個公共的祖先來持有[`AutoField`](../../ref/models/fields.html#django.db.models.AutoField "django.db.models.AutoField"):
```
class Piece(models.Model):
pass
class Article(Piece):
...
class Book(Piece):
...
class BookReview(Book, Article):
pass
```
### Field name “hiding” is not permitted
普通的 Python 類繼承允許子類覆蓋父類的任何屬性。 但在 Django 中,重寫 [`Field`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field")實例是不允許的(至少現在還不行)。如果基類中有一個 `author`字段,你就不能在子類中創建任何名為 `author`的字段。
重寫父類的字段會導致很多麻煩,比如:初始化實例(指定在 `Model.__init__` 中被實例化的字段) 和序列化。而普通的 Python 類繼承機制并不能處理好這些特性。所以 Django 的繼承機制被設計成與 Python 有所不同,這樣做并不是隨意而為的。
這些限制僅僅針對做為屬性使用的 [`Field`](../../ref/models/fields.html#django.db.models.Field "django.db.models.Field")實例,并不是針對 Python 屬性,Python 屬性仍是可以被重寫的。 在 Python 看來,上面的限制僅僅針對字段實例的名稱:如果你手動指定了數據庫的列名稱,那么在多重繼承中,你就可以在子類和某個祖先類當中使用同一個列名稱。(因為它們使用的是兩個不同數據表的字段)。
如果你在任何一個祖先類中重寫了某個 model 字段,Django 都會拋出 [`FieldError`](../../ref/exceptions.html#django.core.exceptions.FieldError "django.core.exceptions.FieldError")異常。
另見
[_The Models Reference_](../../ref/models/index.html)
Covers all the model related APIs including model fields, related
objects, and `QuerySet`.
> 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Model syntax](https://docs.djangoproject.com/en/1.8/topics/db/models/)。
>
> 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。
>
> [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
- 新手入門
- 從零開始
- 概覽
- 安裝
- 教程
- 第1部分:模型
- 第2部分:管理站點
- 第3部分:視圖和模板
- 第4部分:表單和通用視圖
- 第5部分:測試
- 第6部分:靜態文件
- 高級教程
- 如何編寫可重用的應用
- 為Django編寫首個補丁
- 模型層
- 模型
- 模型語法
- 元選項
- 模型類
- 查詢集
- 執行查詢
- 查找表達式
- 模型的實例
- 實例方法
- 訪問關聯對象
- 遷移
- 模式編輯器
- 編寫遷移
- 高級
- 管理器
- 原始的SQL查詢
- 聚合
- 多數據庫
- 自定義查找
- 條件表達式
- 數據庫函數
- 其它
- 遺留的數據庫
- 提供初始數據
- 優化數據庫訪問
- 視圖層
- 基礎
- URL配置
- 視圖函數
- 快捷函數
- 裝飾器
- 參考
- 內建的視圖
- TemplateResponse 對象
- 文件上傳
- 概覽
- File 對象
- 儲存API
- 管理文件
- 自定義存儲
- 基于類的視圖
- 概覽
- 內建顯示視圖
- 內建編輯視圖
- API參考
- 分類索引
- 高級
- 生成 CSV
- 生成 PDF
- 中間件
- 概覽
- 內建的中間件類
- 模板層
- 基礎
- 面向設計師
- 語言概覽
- 人性化
- 面向程序員
- 表單
- 基礎
- 概覽
- 表單API
- 內建的Widget
- 高級
- 整合媒體
- 開發過程
- 設置
- 概覽
- 應用程序
- 異常
- 概覽
- django-admin 和 manage.py
- 添加自定義的命令
- 測試
- 介紹
- 部署
- 概述
- WSGI服務器
- 部署靜態文件
- 通過email追蹤代碼錯誤
- Admin
- 管理操作
- 管理文檔生成器
- 安全
- 安全概述
- 說明Django中的安全問題
- 點擊劫持保護
- 加密簽名
- 國際化和本地化
- 概述
- 本地化WEB UI格式化輸入
- “本地特色”
- 常見的網站應用工具
- 認證
- 概覽
- 使用認證系統
- 密碼管理
- 日志
- 分頁
- 會話
- 數據驗證
- 其它核心功能
- 按需內容處理
- 重定向
- 信號
- 系統檢查框架