模型數據庫
模型是您的數據惟一併且準確的信息來源。它包含您正在儲存的數據的重要字段和行爲。通常來講,每個模型都映射一個數據庫表。django
基礎:app
- 每一個模型都是一個 Python 的類,這些類繼承
django.db.models.Model
- 模型類的每一個屬性都至關於一個數據庫的字段。每一個屬性映射爲一個數據庫列。
- 綜上訴說,Django 給你一個自動生成訪問數據庫的 API
默認狀況下表名是appname_classname,由app名和你建立的模型名組成,能夠自定義。ide
默認狀況下會在表中自動建立一個'id'字段,它是默認主鍵字段,能夠本身手動設置url
一旦你定義了你的模型,你須要告訴Django你準備*使用*這些模型。你須要修改設置文件中的INSTALLED_APPS
,在這個設置中添加包含你 models.py
文件的模塊的名字。spa
字段代理
模型中的每一個字段都應該是相應Field
類的實例 。Django使用字段類類型來肯定一些事情:code
- 字段類型用以指定數據庫數據類型(如:
INTEGER
,VARCHAR
,TEXT
) - 默認的HTML表單輸入框</ ref / forms / widgets>(如:<input type =「text」> <select>)
- 用於Django admin和自動生成表單的基本驗證。
每一個字段都採用一組特定於字段的參數。例如, CharField
(及其子類)須要一個max_length
參數,該 參數指定VARCHAR
用於存儲數據的數據庫字段的大小。還有一些通用參數:orm
null
若是True
,Django將NULL
在數據庫中存儲空值。默認是False
。對象
blank若是True
,該字段容許爲空。默認是False
。
請注意,這不一樣於null
。 null
純粹與數據庫相關,而 blank
與驗證相關。若是字段有blank=True
,則表單驗證將容許輸入空值。若是字段有blank=False
,則須要該字段。
choices
2元組的可迭代(例如,列表或元組),用做此字段的選項。若是給出了這個,則默認表單小部件將是一個選擇框而不是標準文本字段,並將限制對給定選項的選擇。
選項列表以下所示:
YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), )
每一個元組中的第一個元素是將存儲在數據庫中的值。第二個元素由字段的窗體小部件顯示。
給定模型實例,choices
可使用該get_FOO_display()
方法訪問字段的顯示值。
default
字段的默認值。這能夠是值或可調用對象。若是可調用,則每次建立新對象時都會調用它。help_text
使用表單小部件顯示額外的「幫助」文本。即便您的字段未在表單上使用,它也對文檔頗有用。
primary_key若是True
,此字段是模型的主鍵。
若是沒有primary_key=True
爲模型中的任何字段指定,Django將自動添加一個 IntegerField
來保存主鍵,所以primary_key=True
除非要覆蓋默認的主鍵行爲,不然不須要設置 任何字段。主鍵字段是隻讀的。若是更改現有對象上主鍵的值而後保存它,則將建立一個與舊對象並排的新對象。
unique
若是True
,該字段在整個表格中必須是惟一的。
自動主鍵字段
默認狀況下,Django爲每一個模型提供如下字段:
id = models.AutoField(primary_key=True)
這是一個自動遞增的主鍵。
若是您要指定自定義主鍵,只需primary_key=True
在其中一個字段中指定便可 。若是Django看到你明確設置Field.primary_key
,它將不會添加自動 id
列。
每一個模型只須要一個字段primary_key=True
(顯式聲明或自動添加)。
詳細字段名
除了和 以外ForeignKey
, 每一個字段類型都採用可選的第一個位置參數 - 一個詳細的名稱。若是沒有給出詳細名稱,Django將使用字段的屬性名稱自動建立它,將下劃線轉換爲空格。ManyToManyField
OneToOneField
在此示例中,詳細名稱爲:"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, on_delete=models.CASCADE, verbose_name="the related poll", ) sites = models.ManyToManyField(Site, verbose_name="list of sites") place = models.OneToOneField( Place, on_delete=models.CASCADE, verbose_name="related place", )
關係
顯然,關係數據庫的力量在於將表相互關聯。Django提供了定義三種最多見數據庫關係類型的方法:多對一,多對多和一對一。
要定義多對一關係,請使用django.db.models.ForeignKey
。您能夠像使用任何其餘Field
類型同樣使用它:將其包含爲模型的類屬性。
ForeignKey
須要一個位置參數:與模型相關的類。
from django.db import models class Manufacturer(models.Model): # ... pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) # ...
多對多關係
要定義多對多關係,請使用 ManyToManyField
。您能夠像使用任何其餘Field
類型同樣使用它 :將其包含爲模型的類屬性。
ManyToManyField
須要一個位置參數:與模型相關的類。
例如,若是a Pizza
有多個Topping
對象 - 也就是說,a Topping
能夠在多個比薩餅上,每一個Pizza
都有多個澆頭 - 這就是你如何表示:
from django.db import models class Topping(models.Model): # ... pass class Pizza(models.Model): # ... toppings = models.ManyToManyField(Topping)
建議(但不要求)a的名稱 ManyToManyField
(toppings
在上面的示例中)是描述相關模型對象集的複數。
哪一種型號有什麼關係並不重要 ManyToManyField
,但您應該只將其放在其中一種型號中 - 而不是兩種型號。
一般,ManyToManyField
實例應該放在要在表單上編輯的對象中。在上面的例子中, toppings
是Pizza
(而不是Topping
擁有pizzas
ManyToManyField
),由於考慮披薩的配料比在多個披薩上打頂更天然。在上面設置的方式,Pizza
表單將容許用戶選擇澆頭。
多對多關係中的額外字段
當您只處理簡單的多對多關係時,例如混合和匹配比薩餅和澆頭,ManyToManyField
您只須要一個標準 。可是,有時您可能須要將數據與兩個模型之間的關係相關聯。
例如,考慮應用程序跟蹤音樂家所屬的音樂組的狀況。一我的與他們所屬的團體之間存在多對多的關係,所以您可使用a ManyToManyField
來表示這種關係。可是,您可能但願收集的成員資格有不少詳細信息,例如此人加入該組的日期。
對於這些狀況,Django容許您指定將用於管理多對多關係的模型。而後,您能夠在中間模型上添加額外的字段。中間模型與ManyToManyField
使用 through
參數指向將充當中介的模型相關聯 。對於咱們的音樂家示例,代碼看起來像這樣:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
設置中間模型時,您明確指定多對多關係中涉及的模型的外鍵。此顯式聲明定義了兩個模型的關聯方式。
一對一的關係
要定義一對一的關係,請使用 OneToOneField
。您能夠像使用任何其餘Field
類型同樣使用它 :將其包含爲模型的類屬性。
當對象以某種方式「擴展」另外一個對象時,這對於對象的主鍵最有用。
OneToOneField
須要一個位置參數:與模型相關的類。
例如,若是您正在構建「地點」數據庫,您將在數據庫中構建很是標準的內容,例如地址,電話號碼等。而後,若是你想在這些地方創建一個餐館數據庫,而不是重複本身並在Restaurant
模型中複製這些字段,你能夠作Restaurant
一個OneToOneField
to Place
(由於一個餐館「是一個」地方;事實上,處理這一般使用 繼承,它涉及隱式的一對一關係)。
OneToOneField
字段也接受可選 parent_link
參數。
OneToOneField
用於自動成爲模型主鍵的類。這再也不是真的(儘管你能夠手動傳入primary_key
參數)。所以,如今能夠OneToOneField
在單個模型上具備多個類型的字段 。
注意:
因爲Django的查詢查找語法的工做方式,字段名稱不能在一行中包含多個下劃線。例如:
class Example(models.Model): foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
Meta
選項
使用內部提供模型元數據,以下所示: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
),數據庫表名(db_table
)或人類可讀的單數和複數名稱(verbose_name
和 verbose_name_plural
)。不是必要,添加到模型是徹底可選的。
模型屬性
-
objects
模型最重要的屬性是Manager
。它是爲Django模型提供數據庫查詢操做的接口,用於 從數據庫中 檢索實例。若是Manager
未定義自定義,則默認名稱爲objects
。
模型方法
在模型上定義自定義方法,以向對象添加自定義「行級」功能。雖然Manager
方法旨在執行「表格範圍」的事情,但模型方法應該做用於特定的模型實例。
__str__()
Python「魔術方法」,返回任何對象的字符串表示形式。這是Python和Django在模型實例須要被強制並顯示爲純字符串時將使用的內容。最值得注意的是,當您在交互式控制檯或管理員中顯示對象時會發生這種狀況。
你老是想要定義這個方法; 默認狀況下根本沒有用。
get_absolute_url()
這告訴Django如何計算對象的URL。Django在其管理界面中使用它,而且只要它須要找出對象的URL。
具備惟一標識它的URL的任何對象都應定義此方法。
覆蓋預約義的模型方法
還有另外一組模型方法,它們封裝了一些您想要自定義的數據庫行爲。特別是你常常想改變方式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().save(*args, **kwargs) # Call the "real" save() method. do_something_else()
重要的是要記住調用超類方法 - 那就是業務 - 以確保對象仍然保存到數據庫中。若是您忘記調用超類方法,則不會發生默認行爲,也不會觸及數據庫。super().save(*args, **kwargs)
傳遞能夠傳遞給模型方法的參數也很重要 - 這就是位的做用。Django將不時擴展內置模型方法的功能,增長新的參數。若是在方法定義中使用,則能夠保證代碼在添加時自動支持這些參數。*args,**kwargs
*args, **kwargs
繼承模型
模型繼承在Django中與普通類繼承在Python中的工做方式幾乎徹底相同,但也仍有遵循本頁開頭的內容。這意味着其基類應該繼承自django.db.models.Model
。
您必須作出的惟一決定是,您是但願父模型自己是模型(使用本身的數據庫表),仍是父母只是經過子模型可見的公共信息的持有者。
Django中有三種可能的繼承方式。
- 一般,您只想使用父類來保存您不但願爲每一個子模型鍵入的信息。這個類不會被孤立使用,因此抽象基類就是你所追求的。
- 若是你是現有模型的子類(多是徹底來自另外一個應用程序的東西),並但願每一個模型都有本身的數據庫表,那麼 多表繼承是最佳選擇。
- 最後,若是您只想修改模型的Python級行爲,而不以任何方式更改模型字段,則可使用 代理模型。
抽象基類
當您想要將一些公共信息放入許多其餘模型時,抽象基類很是有用。你寫你的基類,並把abstract=True
在元 類。而後,此模型將不用於建立任何數據庫表。相反,當它用做其餘模型的基類時,其字段將添加到子類的字段中。
一個例子:
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模型,由於它是一個抽象基類。它不生成數據庫表或具備管理器,而且沒法直接實例化或保存。
從抽象基類繼承的字段可使用其餘字段或值覆蓋,也可使用刪除None
。
對於許多用途,這種類型的模型繼承將徹底符合您的要求。它提供了一種在Python級別分解公共信息的方法,同時仍然只在數據庫級別爲每一個子模型建立一個數據庫表。
Meta
繼承
當建立抽象基類時,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確實對抽象基類的Meta類進行了一次調整:在安裝Meta屬性以前,它設置了abstract=False
。這意味着抽象基類的子節點自己不會自動成爲抽象類。固然,您能夠建立一個繼承自另外一個抽象基類的抽象基類。您只須要記住abstract=True
每次都明確設置。
多表繼承
Django支持的第二種模型繼承是當層次結構中的每一個模型都是模型自己時。每一個模型對應於本身的數據庫表,能夠單獨查詢和建立。繼承關係引入子模型與其每一個父模型之間的連接(經過自動建立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")
Meta
和多表繼承
在多表繼承狀況下,子類從其父類的Meta類繼承是沒有意義的。全部的Meta選項都已經應用於父類,而且再次應用它們一般只會致使矛盾的行爲(這與基類自己不存在的抽象基類狀況造成對比)。
所以,子模型沒法訪問其父級的Meta類。可是,有一些有限的狀況,子進程從父進程繼承行爲:若是子進程沒有指定 ordering
屬性或 get_latest_by
屬性,它將從其父進程繼承它們。
若是父級有一個排序而你不但願孩子有任何天然順序,你能夠明確地禁用它:
class ChildModel(ParentModel): # ... class Meta: # Remove parent's ordering effect ordering = []
繼承和反向關係
由於多表繼承使用隱式 OneToOneField
連接子項和父項,因此能夠從父項向下移動到子項,如上例所示。可是,這會佔用名稱和 關係的默認related_name
值 。若是要將這些類型的關係放在父模型的子類上,則 必須 在每一個此類字段上指定該屬性。
class Supplier(Place): customers = models.ManyToManyField(Place)
這會致使錯誤,添加related_name
到該customers
字段將解決錯誤:models.ManyToManyField(Place,related_name='provider')
代理模型
使用多表繼承時,會爲模型的每一個子類建立一個新的數據庫表。這一般是所需的行爲,由於子類須要一個位置來存儲基類上不存在的任何其餘數據字段。可是,有時您只想更改模型的Python行爲 - 可能更改默認管理器或添加新方法。
這就是代理模型繼承的用途:爲原始模型建立代理。您能夠建立,刪除和更新代理模型的實例,而且將保存全部數據,就像使用原始(非代理)模型同樣。不一樣之處在於您能夠更改代理中的默認模型排序或默認管理器等內容,而無需更改原始內容。
代理模型聲明爲普通模型。你經過設置類的proxy
屬性告訴Django它是一個代理模型。Meta
True
例如,假設您要向Person
模型添加方法。你能夠這樣作:
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>
你仍然可使用一個代理模型來定義模型的默認排序方法你也許不會想一直對「Persion」進行排序,可是一般狀況下用代理模型根據「姓氏」屬性進行排序這很簡單。:
class OrderedPerson(Person): class Meta: ordering = ["last_name"] proxy = True
如今,正常Person
查詢將是無序的,OrderedPerson
查詢將按順序排序last_name
。
代理繼承和非託管模型之間的差別¶
代理模型繼承可能看起來與使用managed
模型Meta
類的屬性建立非託管模型很是類似。
經過仔細設置,Meta.db_table
您能夠建立一個非託管模型,該模型能夠隱藏現有模型併爲其添加Python方法。可是,若是您進行任何更改,則須要保持兩個副本同步,這將是很是重複和脆弱的。
另外一方面,代理模型的行爲與它們所表明的模型徹底相同。它們始終與父模型同步,由於它們直接繼承其字段和管理器。
通常規則是:
- 若是要鏡像現有模型或數據庫表,而且不想要全部原始數據庫表列,請使用
Meta.managed=False
。該選項一般用於建模不受Django控制的數據庫視圖和表。 - 若是您想要更改模型的僅Python行爲,但保留與原始字段相同的字段,請使用
Meta.proxy=True
。這進行了設置,以便在保存數據時代理模型是原始模型的存儲結構的精確副本。