W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
模型中最重要且唯一必要的是數(shù)據(jù)庫的字段定義。字段在類屬性中定義。定義字段名時應(yīng)小心避免使用與 模型 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, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
模型中每一個字段都應(yīng)該是某個 ??Field
?類的實例, Django 利用這些字段類來實現(xiàn)以下功能:
INTEGER
??, ??VARCHAR
??, ??TEXT
??)。<input type="text">
??, ??<select>
??)。Django 內(nèi)置了數(shù)十種字段類型,如果 Django 內(nèi)置類型不能滿足你的需求,你可以很輕松地編寫自定義的字段類型。
每一種字段都需要指定一些特定的參數(shù)。 例如, ??CharField
??(以及它的子類)需要接收一個 ??max_length
??參數(shù),用以指定數(shù)據(jù)庫存儲 ??VARCHAR
??數(shù)據(jù)時用的字節(jié)數(shù)。一些可選的參數(shù)是通用的,可以用于任何字段類型,下面介紹一部分經(jīng)常用到的通用參數(shù):
null
???:如果設(shè)置為 ??True
??,當(dāng)該字段為空時,Django 會將數(shù)據(jù)庫中該字段設(shè)置為 ??NULL
??。默認(rèn)為 ??False
??。blank
??:如果設(shè)置為 ?True
?,該字段允許為空。默認(rèn)為 ?False
?。注意該選項與 ?null
?不同, ?null
?選項僅僅是數(shù)據(jù)庫層面的設(shè)置,而 ?blank
?是涉及表單驗證方面。如果一個字段設(shè)置為?blank=True
? ,在進(jìn)行表單驗證時,接收的數(shù)據(jù)該字段值允許為空,而設(shè)置為?blank=False
? 時,不允許為空。choices
??:一系列二元組,用作此字段的選項。如果提供了二元組,默認(rèn)表單小部件是一個選擇框,而不是標(biāo)準(zhǔn)文本字段,并將限制給出的選項。??choices
??中一個選項列表:
YEAR_IN_SCHOOL_CHOICES = [
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
]
注意:每當(dāng) ??choices
??的順序變動時將會創(chuàng)建新的遷移。
每個二元組的第一個值會儲存在數(shù)據(jù)庫中,而第二個值將只會用于在表單中顯示。對于一個模型實例,要獲取該字段二元組中相對應(yīng)的第二個值,使用 ??get_FOO_display()?
? 方法。例如:
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'
你也可以使用枚舉類以簡潔的方式來定義 ??choices
??:
from django.db import models
class Runner(models.Model):
MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
name = models.CharField(max_length=60)
medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
default
?:該字段的默認(rèn)值。可以是一個值或者是個可調(diào)用的對象,如果是個可調(diào)用對象,每次實例化模型時都會調(diào)用該對象。help_text
?:額外的“幫助”文本,隨表單控件一同顯示。即便你的字段未用于表單,它對于生成文檔也是很有用的。primary_key
?:如果設(shè)置為 ??True
??,將該字段設(shè)置為該模型的主鍵。在一個模型中,如果你沒有對任何一個字段設(shè)置 ??primary_key=True
?? 選項。 Django 會自動添加一個 ??IntegerField
??字段,并設(shè)置為主鍵,因此除非你想重寫 Django 默認(rèn)的主鍵設(shè)置行為,你可以不手動設(shè)置主鍵。
主鍵字段是只可讀的,如果你修改一個模型實例的主鍵并保存,這等同于創(chuàng)建了一個新的模型實例。例如:
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)
<QuerySet ['Apple', 'Pear']>
??unique
??:如果設(shè)置為 ??True
??,這個字段的值必須在整個表中保持唯一。
默認(rèn)情況下,Django 給每個模型一個自動遞增的主鍵,其類型在 ??AppConfig.default_auto_field
?? 中指定,或者在 ??DEFAULT_AUTO_FIELD
??配置中全局指定。例如:
id = models.BigAutoField(primary_key=True)
如果你想自己指定主鍵, 在你想要設(shè)置為主鍵的字段上設(shè)置參數(shù) ??primary_key=True
??。如果 Django 看到你顯式地設(shè)置了 ??Field.primary_key
??,將不會自動在表(模型)中添加 ??id
??列。每個模型都需要擁有一個設(shè)置了 ??primary_key=True
?? 的字段(無論是顯式的設(shè)置還是 Django 自動設(shè)置)。
在舊版本中,自動創(chuàng)建的主鍵字段總是 ??AutoField
??。
除了 ??ForeignKey
??, ??ManyToManyField
??和 ??OneToOneField
??,任何字段類型都接收一個可選的位置參數(shù) ??verbose_name
??,如果未指定該參數(shù)值, Django 會自動使用字段的屬性名作為該參數(shù)值,并且把下劃線轉(zhuǎn)換為空格。
在該例中:備注名為??"?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
??接收的第一個參數(shù)為模型的類名,后面可以添加一個 ??verbose_name
??參數(shù):
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",
)
慣例是不將 ??verbose_name
??的首字母大寫,必要時 Djanog 會自動把首字母轉(zhuǎn)換為大寫。
顯然,關(guān)系型數(shù)據(jù)庫的強大之處在于各表之間的關(guān)聯(lián)關(guān)系。 Django 提供了定義三種最常見的數(shù)據(jù)庫關(guān)聯(lián)關(guān)系的方法:多對一,多對多,一對一。
定義一個多對一的關(guān)聯(lián)關(guān)系,使用 ??django.db.models.ForeignKey
?? 類。就和其它 ?Field?字段類型一樣,只需要在你模型中添加一個值為該類的屬性。??ForeignKey
??類需要添加一個位置參數(shù),即你想要關(guān)聯(lián)的模型類名。例如,如果一個 ??Car
??模型有一個制造者 ??Manufacturer
??--就是說一個 ??Manufacturer
??制造許多輛車,但是每輛車都僅有一個制造者-- 那么使用下面的方法定義這個關(guān)系:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
你也可以創(chuàng)建 自關(guān)聯(lián)關(guān)系 (一個模型與它本身有多對一的關(guān)系)和 與未定義的模型間的關(guān)聯(lián)關(guān)系 。建議設(shè)置 ??ForeignKey
??字段名(上例中的 ??manufacturer
??)為想要關(guān)聯(lián)的模型名,但是你也可以隨意設(shè)置為你想要的名稱,例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
)
# ...
定義一個多對多的關(guān)聯(lián)關(guān)系,使用 ??django.db.models.ManyToManyField
?類。就和其他 ??Field
??字段類型一樣,只需要在你模型中添加一個值為該類的屬性。??ManyToManyField
??類需要添加一個位置參數(shù),即你想要關(guān)聯(lián)的模型類名。例如:如果 ??Pizza
??含有多種 ??Topping
??(配料) -- 也就是一種 ??Topping
??可能存在于多個 ??Pizza
??中,并且每個 ??Pizza
??含有多種 ??Topping
??--那么可以這樣表示這種關(guān)系:
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
和 ??ForeignKey
??類一樣,你也可以創(chuàng)建 自關(guān)聯(lián)關(guān)系 (一個對象與他本身有著多對多的關(guān)系)和 與未定義的模型的關(guān)系 。建議設(shè)置 ??ManyToManyField
??字段名(上例中的 ??toppings
??)為一個復(fù)數(shù)名詞,表示所要關(guān)聯(lián)的模型對象的集合。對于多對多關(guān)聯(lián)關(guān)系的兩個模型,可以在任何一個模型中添加 ??ManyToManyField
??字段,但只能選擇一個模型設(shè)置該字段,即不能同時在兩模型中添加該字段。一般來講,應(yīng)該把 ??ManyToManyField
??實例放到需要在表單中被編輯的對象中。在之前的例子中, ??toppings
??被放在 ??Pizza
??當(dāng)中(而不是 ??Topping
??中有指向 ??pizzas
??的 ??ManyToManyField
??實例 )因為相較于配料被放在不同的披薩當(dāng)中,披薩當(dāng)中有很多種配料更加符合常理。按照先前說的,在編輯 ??Pizza
??的表單時用戶可以選擇多種配料。
如果你只是想要一個類似于記錄披薩和配料之間混合和搭配的多對多關(guān)系,標(biāo)準(zhǔn)的 ??ManyToManyField
??就足夠你用了。但是,有時你可能需要將數(shù)據(jù)與兩個模型之間的關(guān)系相關(guān)聯(lián)。舉例來講,考慮一個需要跟蹤音樂人屬于哪個音樂組的應(yīng)用程序。在人和他們所在的組之間有一個多對多關(guān)系,你可以使用 ??ManyToManyField
??來代表這個關(guān)系。然而,你想要記錄更多的信息在這樣的關(guān)聯(lián)關(guān)系當(dāng)中,比如你想要記錄某人是何時加入一個組的。對于這些情況,Django 允許你指定用于控制多對多關(guān)系的模型。你可以在中間模型當(dāng)中添加額外的字段。在實例化 ??ManyToManyField
??的時候使用 ??through
??參數(shù)指定多對多關(guān)系使用哪個中間模型。對于我們舉的音樂家的例子,代碼如下:
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)
你需要在設(shè)置中間模型的時候,顯式地為多對多關(guān)系中涉及的中間模型指定外鍵。這種顯式聲明定義了這兩個模型之間是如何關(guān)聯(lián)的。
在中間模型當(dāng)中有一些限制條件:
Group
??)的外鍵,要么你必須通過 ??ManyToManyField.through_fields
?? 參數(shù)在多個外鍵當(dāng)中手動選擇一個外鍵,如果有多個外健且沒有用 ??through_fields
?參數(shù)選擇一個的話,會出現(xiàn)驗證錯誤。對于指向目標(biāo)模型(我們例子當(dāng)中的 ??Person
??)的外鍵也有同樣的限制。through_fields
??參數(shù),要不然會出現(xiàn)驗證錯誤。現(xiàn)在你已經(jīng)通過中間模型完成你的 ??ManyToManyField
??(例子中的 ??Membership
??),可以開始創(chuàng)建一些多對多關(guān)系了。你通過實例化中間模型來創(chuàng)建關(guān)系:
>>> 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()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<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()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
你也可以使用 ??add()?
?, ??create()
??, 或者 ??set()?
? 創(chuàng)建關(guān)系,只要你為任何必需的字段指定 ??through_defaults
??
>>> beatles.members.add(john, through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.create(name="George Harrison", through_defaults={'date_joined': date(1960, 8, 1)})
>>> beatles.members.set([john, paul, ringo, george], through_defaults={'date_joined': date(1960, 8, 1)})
你可能更傾向直接創(chuàng)建中間模型。如果自定義中間模型沒有強制? ?(model1, model2)?
? 對的唯一性,調(diào)用 ??remove()?
? 方法會刪除所有中間模型的實例:
>>> Membership.objects.create(person=ringo, group=beatles,
... date_joined=date(1968, 9, 4),
... invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>
方法 ??clear()?
? 用于實例的所有多對多關(guān)系:
>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
一旦你建立了自定義多對多關(guān)聯(lián)關(guān)系,就可以執(zhí)行查詢操作。和一般的多對多關(guān)聯(lián)關(guān)系一樣,你可以使用多對多關(guān)聯(lián)模型的屬性來查詢:
# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>
當(dāng)你使用中間模型的時候,你也可以查詢他的屬性:
# 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))
<QuerySet [<Person: Ringo Starr]>
如果你想訪問一個關(guān)系的信息時你可以直接查詢 ??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
?對象來查詢多對多遞歸關(guān)聯(lián)關(guān)系 :
>>> 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
??來定義一對一關(guān)系。就像使用其他類型的 ??Field
??一樣:在模型屬性中包含它。當(dāng)一個對象以某種方式“繼承”另一個對象時,這對該對象的主鍵非常有用。??OneToOneField
??需要一個位置參數(shù):與模型相關(guān)的類。例如,當(dāng)你要建立一個有關(guān)“位置”信息的數(shù)據(jù)庫時,你可能會包含通常的地址,電話等字段。接著,如果你想接著建立一個關(guān)于關(guān)于餐廳的數(shù)據(jù)庫,除了將位置數(shù)據(jù)庫當(dāng)中的字段復(fù)制到 ??Restaurant
??模型,你也可以將一個指向 ??Place OneToOneField
?? 放到 ??Restaurant
??當(dāng)中(因為餐廳“是一個”地點);事實上,在處理這樣的情況時最好使用 模型繼承 ,它隱含的包括了一個一對一關(guān)系。和 ??ForeignKey
??一樣,可以創(chuàng)建 自關(guān)聯(lián)關(guān)系 也可以創(chuàng)建 與尚未定義的模型的關(guān)系 。
??OneToOneField
??字段還接受一個可選的 ??parent_link
??參數(shù)。??OneToOneField
?? 類通常自動的成為模型的主鍵,這條規(guī)則現(xiàn)在不再使用了(然而你可以手動指定 ??primary_key
??參數(shù))。因此,現(xiàn)在可以在單個模型當(dāng)中指定多個 ??OneToOneField
??字段。
關(guān)聯(lián)另一個應(yīng)用中的模型是當(dāng)然可以的。為了實現(xiàn)這一點,在定義模型的文件開頭導(dǎo)入需要被關(guān)聯(lián)的模型。接著就可以在其他有需要的模型類當(dāng)中關(guān)聯(lián)它了。比如:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = models.ForeignKey(
ZipCode,
on_delete=models.SET_NULL,
blank=True,
null=True,
)
Django 對模型的字段名有一些限制:
1、一個字段的名稱不能是 Python 保留字,因為這會導(dǎo)致 Python 語法錯誤。比如:
class Example(models.Model):
pass = models.IntegerField() # 'pass' is a reserved word!
2、一個字段名稱不能包含連續(xù)的多個下劃線,原因在于 Django 查詢語法的工作方式。比如:
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
3、字段名不能以下劃線結(jié)尾,原因同上。
但是,這些限制是可以被解決的,因為字段名沒要求和數(shù)據(jù)庫列名一樣。
SQL保留字,例如 ??join
??, ??where
??或 ??select
??, 是 可以被用在模型字段名當(dāng)中的,因為 Django 在對底層的 SQL 查詢當(dāng)中清洗了所有的數(shù)據(jù)庫表名和字段名,通過使用特定數(shù)據(jù)庫引擎的引用語法。
如果已經(jīng)存在的模型字段不能滿足你的需求,或者你希望支持一些不太常見的數(shù)據(jù)庫列類型,你可以創(chuàng)建自己的字段類。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: