Django Model 數據表
Django Model 定義語法
版本:1.7
主要來源:https://docs.djangoproject.com/en/1.7/topics/db/models/
簡單用法
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
會自動生成SQL:
CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
-默認會根據APP的名稱生成"app名稱"+"_"+"類名"
-會自動增加ID字段
-Django會根據settings配置中指定的數據庫類型來生成相應的SQL語句
使用Model
要想使用Model需要在settings配置中INSTALLED_APPS中增加你的APP,例如,你的models在myapp.models中,那麽就應該使用:
INSTALLED_APPS = (
#...
‘myapp‘,
#...
)
在將app添加到INSTALLD_APPS中後需要執行manage.py migrate,也可通過manage.py makemigrations進行遷移。
Fields
Django提供了各種數據字段類型。需要註意不要使用與API相沖突的字段名稱如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 types
django會根據field類型確定
- 數據庫字段類型(如INTEGER,VARCHAR)
- 默認的HTML生成什麽樣的表單項
- 最低限度的驗證需求。它被用在 Django 管理後臺和自動生成的表單中。
field是可以自定義的。
Field options
每個field類型都有自己特定的參數,但也有一些通用的參數,這些參數都是可選的:
null
如果為 True , Django 在數據庫中會將空值(empty)存儲為 NULL 。默認為 False 。
blank
設置字段是否可以為空,默認為False(不允許為空)
和null的區別在於:null是數據庫的範圍,而blank是用於驗證。如果一個字段的 blank=True ,Django 在進行表單數據驗證時,會允許該字段是空值。如果字段的 blank=False ,該字段就是必填的。
choices
它是一個可叠代的二元組(例如,列表或是元組),用來給字段提供選擇項。如果設置了 choices, Django會顯示選擇框,而不是標準的文本框,而且這個選擇框的選項就是 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
u‘L‘
>>> p.get_shirt_size_display()
u‘Large‘
default
默認值,可以是一個具體的值也可以是一個對象,每次調用次會創建一個新的對象
help_text
附加的幫助信息。在使用表單時會顯示在字段下面。即使不使用表單也可以起來幫助文檔的作用。
primary_key
如果為True,則表示這個字段是主鍵。
如果你沒有設置主鍵,Django會自動創建一個自增的IntergerField類型主鍵,可以通過自定義主鍵來覆蓋默認的行為。
unique
如果為 True ,那麽字段值就必須是全表唯一的。
Automatic primary key fields
默認情況下,Django 會給每個 model 添加下面這個字段:
id = models.AutoField(primary_key=True)
這是一個自增主鍵字段。
如果你想指定一個自定義主鍵字段,只要在某個字段上指定 primary_key=True 即可。如果 Django 看到你顯式地設置了 Field.primary_key,就不會自動添加 id 列。
每個 model 只要有一個字段指定 primary_key=True 就可以了。(可以自定義也可以保持默認自動增加的主鍵)
Verbose field names(詳細名稱)
每個字段的類型,除了ForeignKey, ManyToManyField 和 OneToOneField外,還有一個可選的第一位置參數,這個參數用於標記字段的詳細名稱。如果Verbose field names沒有顯示的給出,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
需要使用第一參數,所以可以顯示的使用 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會自動利用第一個參數。
Relationships(關系)
Django支持關系數據庫中常用的many-to-one,many-tomany,one-to-one
Many-to-one(多對一關系)
定義Many-to-one關系,使用django.db.models.ForeignKey。使用方法和其他字段類型一樣:在model中定義時包含ForeignKey屬性。
ForeignKey必須要一個參數:需要鏈接到哪個model.
例如:一輛汽車(car)和汽車制造商(manufacturer)的關系,那麽一個汽車制造商會有制造多輛車,但每輛車卻只能有一個汽車制造商:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
你也可以定義一個遞歸的關系(在對象內容實部Many-to-one的定義)和relationships to models not yet defined(沒看明白,看完模型關系後再修改);
建議但不強制要求ForeignKey字段的名字是模型的小寫字母的名字(例如在上例中使用的manufacturer)。當然你可以使用任何你想要的名字,例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
Many-to-many(多對多關系)
定義Many-to-many關系,使用django.db.models.ManyToManyField.使用方法和其他字段類型一樣:在model中定義包含ManyToManyField屬性。
ManyToManyField必須要一個參數:需要鏈接到那個model.
例如,一個Pizza有多個Topping對象——也就是一個Topping可以在多個Pizza上,每個Pizza有多個Toppings——這種情況我們可以這樣定義:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
和ForeignKey一樣,可以創建遞歸關系(在對象內部實現Many-to-many的定義)和relationships to models not yet defined
建議但不強制要求ManyToManyField的名字(上面的例子中的toppings)是復數形式,復數形式是為了描述相關模型對象的集合。
哪個模型帶有ManyToManyField都沒關系,但你只能在其中一個模型中使用它,而不能在兩個模型中都使用它。
一般來說,ManyToManyField實例應該包含在使用表單編輯的對象中,在上面的例子中,toppings在Pizza中(而不是Topping有pizzas ManyToManyField),因為一個pizzas有多個Topping,比一個Topping在多個pizzas上更容易讓人理解。這就是上面我們使用的方式,Pizza管理表單將讓用戶選擇那種Topping。
還有一些可選參數。
Many-to-many關系的額外字段
如果只需要處理簡單的多對多關系,就像上面pizzas和topping的關系,那麽ManyToManyField字段就可以滿足需要,然而,有些時候你需要讓數據在兩個模型之間產生聯系。
例如,考慮一下跟蹤樂隊和樂隊擁有的音樂家的應用程序的例子。這是一個人和這個人所在團隊之間的多對多關系,因此你可以使用ManyToManyField來描述這個關系。然而,這種成員關系有很多你想要搜集的細節,比如這個人加入團隊的時間。
對於這種情況,Django讓你可以指定用來管理多對多關系的模型。然後你可以在中間模型中放入額外的字段。中間模型使用through參數指向像中間人一樣工作的模型,來和ManyToManyField發生關系。對於四個音樂家的例子,代碼可能像這樣子:
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來明確指定外鍵關系。如果你有多個外鍵,但沒有指定through_fields,會產生校驗錯誤。類似的限制適用於外鍵的目標model(例如Person)
對於一個model通過中間model實現多對多關系,兩個到同一模型的外鍵是允許的,但會被認為是多對多關系的兩個不同側面。如果有兩個或以上的外鍵定義,你必須要定義through_fields,否則會產生校驗錯誤。
當使用中間模型來定義一個到自己的多對多關系的模型時,你必須使用symmetrical=False(參閱“模型字段參考”)。
現在你已經建立了ManyToManyField來使用中間模型(在這個例子中是MemberShip),你可以開始創建一些多對多的對應關系了。具體來說是創建中間模型的一些實例:
現在已經在中間model中設置了ManyToManyField(例子中的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>]
不同於一般的many-to-many關系字段,你不能通過直接通過關系對象進行增加、創建或賦值(即: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關系之外的一些細節,這些細節在中間model--Membership中定義,而不僅僅只是簡單創建了Person和Group之間的關系。類似關系的唯一解決辦法是創建中間model
基於同樣的原因 remove() 也是被禁用的,但可以通過 clear() 清除所有多對多關系實例:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
[]
一旦創建了中間model實例,並建立了一個多對多關系實例,就可以和正常的多對多關系一樣進行查詢:
# 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’s 信息,你可以這樣做直接查詢Membership:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u‘Needed a new drummer.‘
當然你也可以通過多對多的反向關系從Person 實例進行查詢:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
u‘Needed a new drummer.‘
One-to-one關系
要定義一對一關系,請使用OneToOneField。它的使用方法和其它字段一樣:把它包含在模型的類屬性中。
當對象以某種方式擴展了另一個對象的主鍵時,對象的主鍵是最重要的。
OneToOneField要求一個位置參數:該模型相關的類。
例如,如果你將創建一個數據表places,你可能會在數據表中建立一些相當標準的東西,就像地址、電話號碼等等。然後,如果你想在places上建立一個飯館,你可以不必重復勞動,在Restaurant模型中復制這些字段,你可以建立一個帶有到Place的OneToOneField(因為飯館就是一個place;實際上,對於這種情況典型的做法是使用繼承,這實際上是一種隱式的一對一關系)。
和外鍵一樣,你可以定義循環關系和到未定義模型的關系;
OneToOneField也接受一個可選的參數parent_link 這個參數在“模型字段參考”有介紹。
OneToOneField類曾經會自動成為模型的主鍵。現在情況不再如此了(如果你願意你可以手動傳遞一個primary_key參數)。因此,現在一個模型可以擁有多個OneToOneField類型的字段。
Models across files(跨文件的model)
在當前model和另一個應用程序中的model建立關系是沒有問題的,只需要引入相關的model,然後在需要的時候使用就可以了:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(ZipCode)
Field name restrictions(字段名限制 )
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!
這些限制是可以繞過的,因為你的字段名並不需要和數據表的列名匹配。
SQL保留字,比如join、where或select,可以用作模型字段名,因為Django在進行底層的SQL查詢前會對所有數據表名和列名進行轉義。
Custom field types(自定義字段類型)
如果現有的字段類型不能滿足你的需要,或者你使用的數據庫具有一些特殊的類型,你可以創建自己字段類型。
Meta options
定義model的metadata(元數據)是通過使用一個內部類Meta,例:
from django.db import models
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這些附加選項,都可以放到class Meta中,這些選項都不是必需的。
Model attributes
objects
這是model最重要的Manager屬性,通過它查詢數據庫並於model之間形成映射。如果沒有自定義manager,默認名稱為objects。managers只能通過model類,而不能通過model類實例來訪問。
Model methods
通過model自定義方法添加自定義"行級"功能,而managers方法是為“表級”添加自定義功能,model自定義方法可以通過model實例來使用。
這樣就可以把業務邏輯都放在一個model裏。例如:
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)
上個實例的最後一個方法是屬性。
這個model實例繼承自models.Model,會自動具備大量的方法,可以覆蓋大部分的方法,但有幾個卻是必須的:
str() (Python 3)
class Tag(models.Model): name = models.CharField(unique=True,max_length=32)
class Tag(models.Model): name = models.CharField(unique=True,max_length=32) def __str__(self): return self.name class Meta: verbose_name = "標簽" verbose_name_plural = "標簽"
在Python3中相當於unicode()
unicode() (Python 2)
這是一個Python的"魔術方法",它以unicode方式返回任何對象的陳述。Python和Django需要輸出字符串陳述時使用。例如在交互式控制臺或管理後臺顯示的輸出陳述。
默認的實現並不能很好的滿足需要,所以最好自定義這個方法,
get_absolute_url()
定義對象的URL.在管理界面中,任何時候都可以通過URL找到一個對象。
任何通過URL訪問的對象,都應該有唯一標識。
Overriding predefined model methods
還有一些model方法封裝了一些行為。當你想自定義數據庫行為,尤其是想改變save() 和 delete()方式的時候。
你可以自由地重寫這些方法來改變行為。
如果你想在保存對象的時候,覆蓋內置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),以確保對象仍然被保存到數據庫中,如果你忘記調用父類的方法,默認的行為不會發生,數據庫也不會發生改變。
隨著時間的推移,django會增加或擴展一些新的方法,如果你使用args, *kwargs作為你的方法參數,就必須要保證能正確處理這些參數的增加。
覆蓋方法大多數不會用於批量操作
delete()並不一定是在調用一個QuerySet批量刪除時被觸發。為了確保自定義的刪除邏輯被執行,則需要使用 pre_delete and/or post_delete 信號。
不幸的是,還沒有一個好的方法用於批量的創建和更新,因為沒有save()、pre_save、post_save會被調用。
Executing custom SQL
另外一種常見的模式是在model方法和module-level方法中執行自定義SQL。
Model inheritance(Model繼承)
Model的繼承和普通的Python類繼承幾乎相同,但基類的繼承必須是django.db.models.Model.
在Django中有三種繼承風格:
1、如果你想用父類來保存每個子類共有的信息,並且這個類是不會被獨立使用的,那麽應該使用抽象基類。
2、如果你繼承現有model(甚至可能這個類是在另一個應用程序中),並希望每個model都擁有自己對應的數據庫,那就應該使用多表繼承。
3、如果你只是想修改model的Python-level行為,而不改變models fields,則使用代理模式。
Abstract base classes
當你想集中一些公共信息,可以使用虛類。你需要在model的元數據中設置 abstract=True ,這個model將不會被用於創建任何數據表。相反,當他被作為其他子類的基類時,他的字段將被添加到這些子類中。如果抽象類和他的子類有相同名稱的字段會產生一個異常。
例如:
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 model一樣使用,因為他是一個抽象基類。他不會產生一個數據表或者擁有一個管理器,也不能被實例化或直接調用。
在許多情況下這種類型的繼承正是你想要的,它提供了一種用來分解公共信息的方法,雖然只能實現數據表級別創建子模型。
Meta inheritance
當創建一個抽象基類的時候,Django允許在基類中聲明各種Meta屬性,如果子類沒有聲明自己的Meta元數據,他將繼承父類的。如果子類想擴展父類的Meta元數據,則可以繼承父類的Meta。例如:
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對於抽象基類的元數據調整只應該在安裝之前設置abstract=false。這意味著從抽象基類派生的子類不會自動轉型成抽象類本身。當然你也可以繼承來自別一個抽象基類。只需要記住abstract=True每次都應該明確設置。(這段沒怎麽看明白)
在抽象基類中某些屬性幾乎是沒有任何意義的,包括父類的元數據。例如使用db_table將意味著所有的子類(那些沒有指定自己的Meta)將使用同一數據庫,這肯定不會是你想要的。
Be careful with related_name(註意抽象基類中的反向關系名稱定義)
如果你在ForeignKey和ManyToManyField的屬性中使用related_name,你必須為字段指定一個唯一反向關系名稱。在抽象基類中會有一些問題,因為抽象基類的每個字段都會被包括在他的每一個子類中,包括related_name.
要解決這個問題,應該在抽象基類中使用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
然後另一個APP rare/models.py:
from common.models import Base
class ChildB(Base):
pass
在這個示例中 common.ChildA.m2m 字段的反向關系名稱是common_childa_related,而common.ChildB.m2m 的關系名稱ommon_childb_related。這是因為使用了‘%(app_label)s‘ 和 ‘%(class)s‘產生了不同的反向關系名稱。如果你定義了related_name,但忘記了使用‘%(app_label)s‘ 和 ‘%(class)s‘ Django會系統檢查或運行migrate時引發錯誤。
如果沒有在抽象基類的字段中定義related_name屬性,默認關系名稱將是子類名稱+"_set"。通常related_name會被直接在子類的字段屬性中被定義。例如上例中,如果related_name屬性被省略。common.ChildA.m2m 的反向關系名稱應該是childa_set,common.ChildB.m2m的反向關系名稱應該是childb_set。
Multi-table inheritance(多表繼承)
Django支持的第二種model繼承是多表繼承,在繼承結構中每個model都是獨立的。都對應著自己的數據庫表,可以進行獨立的查詢等操作。繼承關系實際是子model和每個父model之間的關系(通過自動創建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對象,那麽就可以使用Place對象通過關系獲得Restaurant對象:
>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
但如果place對象所對應的Restaurant對象不存在,則會引發 Restaurant.DoesNotExist 異常。
Meta and multi-table inheritance
在多表繼承的情況下繼承父類的Meta是沒有意義的。所有的Meta都已經被應用到父類,再應用這些Meta只會導致矛盾。
所以子model不能訪問到父model的Meta,然而也有少數的情況下,子model會從父model中繼承一些行為,例如子model沒有指定 ordering或 get_latest_by屬性,那麽就會從父model中繼承。
如果父model中有一個排序,但你不希望子model有任何的排序規劃,你可以明確的禁用:
class ChildModel(ParentModel):
# ...
class Meta:
# Remove parent‘s ordering effect
ordering = []
Inheritance and reverse relations(繼承與反向關系)
因為多表繼承實際是隱式的使用OneToOneField來鍵接父Model和子model,在這種關系有可能會使用父model來調用子model,比如上面的例子。但是如果你把ForeignKey和ManyToManyField關系應用到這樣一個繼承關系中,Django會返回一個驗證錯誤,必須要指定一個related_name字段屬性。
例如上面的例子,我們再創建一個子類,其中包含一個到父model的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‘).
Specifying the parent link field
如上所述,Django會自動創建一個OneToOneField鏈接你的子model和任何非抽象父model。如果你想自定義子model鍵接回父model的屬性名稱,你可以創建自己的OneToOneField並設置parent_link=True,表示這個字段是對父model的回鏈。
Proxy models
當使用多表繼承時一個新的數據表model會在每一個子類中創建,這是因為子model需要存儲父mdoel不存在的一些數據字段。但有時只需要改變model的操作行為,可能是為了改變默認的管理行為或添加新的方法。
這時就應該使用代理模式的繼承:創建原始model的代理。你可以創建一個用於 create, delete 和 update的代理model,使用代理model的時候數據將會真實保存。這和使用原始model是一樣的,所不同的是當你改變model操作時,不需要去更改原始的model。
代理模式的聲明和正常的繼承聲明方式一樣。你只需要在Meta class 中定義proxy為True就可以了。
例如,你想為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所對應的真實數據表。可通過MyPerson進行所有相應的操作:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
你也可以使用代理模式來定義model的不同默認排序,例如:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
這樣當使用原始model查詢時結果是無序的,而使用OrderedPerson進行查詢時將按last_name進行排序。
QuerySets still return the model that was requested(QuerySets的類型依然會是原始model類型)
當你通過MyPerson來查詢Person對象時,返回的QuerySet依然會是Person對象類型的集合。使用代理模式的model是依靠原始model的,是原始model的擴展。而不是用來替代父model。
Base class restrictions(基類限制)
代理model必須繼承一個非抽像model。
不能從多個非抽像model繼承,代理模式不能為不同model之間創建鏈接。
代理模式可以從任意沒有定義字段的抽象model繼承。
Proxy model managers
如果沒有指定代理model的管理器(managers),它將繼承父類的管理行為。如果你定義了代理model的管理器,它將會成為默認的,當然父類中定義的定義的任何管理器仍然是可以使用的。
繼續上面的例了,增加一個默認的管理器:
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
可以通過創建一個含有新的管理器並進行繼承,來增加一個新的管理器,而不需要去改變更有的默認管理器。
# 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
Differences between proxy inheritance and unmanaged models
代理model看起來很像一個在Meta class中設置了manged的非托管模式model。但實際上這兩種方案是不太一樣,應該考慮在不同的情況下使用那一個:
兩者區別在於:你可以設置model的Meta.managed=False以及通過Meta.db_table指定數據表有創建非托管模式model,並對其添加各種方法,但如果你可保持非托管模式和真實數據表之間的同步,做任何更改都將是很麻煩的事。
而代理model主要用於管理model的各種行為或方法,他們將繼承父model的管理器等。
曾經嘗試將兩種模式合並,但由於API會變得非常復雜,並且難以理解,所以現在是分離成兩種模式:
一般的使用規劃是:
1、如果正使用現有的數據表,但不想在Django中鏡像所有的列,所以應該使用Meta.managed=False,通過這個選項使不在django控制下的數據表或視圖是可用的。
2、如果你想改變一個model的操作行為,但希望保持原始model不被改變,就應該使用Meta.proxy=True.
Multiple inheritance(多重繼承)
和Python的繼承方式一樣,django中的model也可以從多個父model繼承,當然也和Python的繼承方式 一樣,如果出現相同名字的時候只有第一個將被使用。例如:如果多個父model中都包含Meta類,將只有第一個將被使用,其他會被忽略。
通常情況下是不會用到多重繼承的。主是用於“混合式”model:增加一個特殊的額外字段或方式是由多個父model組合而來。應該盡量保持繼承層次的簡單,不然會很難排查某個信息是從那裏來的。
在django1.7以前,多個父model中有id主鍵字段時雖然不會引發錯誤,但有可能導致數據的丟失。例如像下面的model:
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 在父model中:
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:
class Piece(models.Model):
pass
class Article(Piece):
...
class Book(Piece):
...
class BookReview(Book, Article):
pass
Field name “hiding” is not permitted
正常的Python類繼承,允許一個子類覆蓋父類的任何屬性。在Django中是不允許覆蓋父類的屬性字段的。如果一個父類中定義了一個叫author的字段,你就不能在子model中創建別一個叫author的字段。
這種限制僅適用於字段(field),普通的python屬性是可以的。也有一種情況是可以覆蓋的:多表繼承的時候進行手動指定數據庫列名,可以出現子model和父model有同名的字段名稱,因為他們屬於不同的數據表。
如果你覆蓋了父model的字段屬性,django會拋出FieldError異常。
Django Model 數據表