Django 模型定義
Django 模型是使用 Python 代碼對數(shù)據(jù)庫中數(shù)據(jù)的描述,是數(shù)據(jù)的結(jié)構(gòu)年扩,包含數(shù)據(jù)的字段和操作方法。
用 Python 代碼來定義模型
- 每個模型都是
django.db.models.Model
的子類 - Django 自動生成訪問數(shù)據(jù)庫的 API
- 一個模型對應(yīng)數(shù)據(jù)庫中的一張表(多對多關(guān)系時兵志,會生成一張額外的表來管理兩者之間的關(guān)系)
- 模型的屬性對應(yīng)的數(shù)據(jù)庫表中的一列
- 屬性的名稱對應(yīng)著列的名稱
- 字段的類型對應(yīng)著數(shù)據(jù)庫中列的類型
- 數(shù)據(jù)庫中生成的表汤功,默認(rèn)格式是 '應(yīng)用名稱 + 下劃線 + 小寫類名'
- 如果沒有指定主鍵,Django 會自動為定義一個自增量整數(shù)主鍵字段弃理,名字叫 ID
示列代碼:
from django.db import models
class BlogArticles(models.Model):
title = models.CharField(max_length=64)
# 在數(shù)據(jù)庫生成 blog_blogarticles 表
字段
字段在 Python 中表現(xiàn)為類屬性 , 是模型中必不可少的溃论。
字段名稱的限制
- 字段名稱不能是 Python 保留字
- 由于 Django 中雙下劃線是查詢語法,所以字段名稱不能包含多個連續(xù)的下劃線
字段類型的作用
模型中的字段都是 Field 類的實(shí)例痘昌,字段有以下作用:
- 決定數(shù)據(jù)庫列的類型(如 INTEGER 钥勋、 VARCHAR )
- 確定在 Django 的表單和管理后臺中使用什么組件(widget)(如
<input type="text" />
)。 - 在 Admin 后臺和自動生成的表單中做最基本的數(shù)據(jù)驗(yàn)證
- 字段都接受選項(xiàng)參數(shù)
模型字段類表
字段類 | 默認(rèn)小組件 | 說明 |
---|---|---|
AutoField | N/A | 根據(jù) ID 自動遞增的 IntegerField |
BigIntegerField | NumberInput | 64 位整數(shù)控汉,與 IntegerField 很像笔诵,但取值范圍是 -9223372036854775808 到 9223372036854775807 。 |
BinaryField | N/A | 存儲原始二進(jìn)制數(shù)據(jù)的字段姑子。只支持 bytes 類型乎婿。注意,這個字段的功能有限街佑。 |
BooleanField | CheckboxInput | 真假值字段谢翎。如果想接受 null 值,使用 NullBooleanField 沐旨。 |
CharField | TextInput | 字符串字段森逮,針對長度較小的字符串。大量文本應(yīng)該使用 TextField 磁携。有個額外的必須參數(shù):max_length 褒侧,即字段的最大長度(字符個數(shù))。 |
DateField | DateInput | 日期谊迄,在 Python 中使用 datetime.date 實(shí)例表示闷供。有兩個額外的可選參數(shù): auto_now ,每次保存對象時自動設(shè)為當(dāng)前日期 auto_now_add 统诺,創(chuàng)建對象時自動設(shè)為當(dāng)前日期歪脏。 |
DateTimeField | DateTimeInput | 日期和時間,在 Python 中使用 datetime.datetime 實(shí)例表示粮呢。與 DateField 具有相同的額外參數(shù)婿失。 |
DecimalField | TextInput | 固定精度的小數(shù)钞艇,在 Python 中使用 Decimal 實(shí)例表示。有兩個必須的參數(shù): max_digits 和 decimal_places 豪硅。 |
DurationField | TextInput | 存儲時間跨度哩照,在 Python 中使用 timedelta 表示。 |
EmailField | TextInput | 一種 CharField 舟误,使用 EmailValidator 驗(yàn)證輸入葡秒。max_length 的默認(rèn)值為 254 。 |
FileField | ClearableFileInput | 文件上傳字段嵌溢。詳情見下面眯牧。 |
FilePathField | Select | 一種 CharField ,限定只能在文件系統(tǒng)中的特定目錄里選擇文件赖草。 |
FloatField | NumberInput | 浮點(diǎn)數(shù)学少,在 Python 中使用 float 實(shí)例表示。注意秧骑, field.localize 的值為 False 時版确,默認(rèn)的小組件是 TextInput 。 |
ImageField | ClearableFileInput | 所有屬性和方法都繼承自 FileField 乎折,此外驗(yàn)證上傳的對象是不是有效的圖像绒疗。增加了 height 和 width 兩個屬性。需要 Pillow 庫支持骂澄。 |
IntegerField | NumberInput | 整數(shù)吓蘑。取值范圍是 -2147483648 到 2147483647 ,在 Django 支持的所有數(shù)據(jù)庫中可放心使用坟冲。 |
GenericIPAddressField | TextInput | IPv4 或 IPv6 地址磨镶,字符串形式(如 192.0.2.30 、2a02:42fe::4 )健提。 |
NullBooleanField | NullBooleanSelect | 類似于 BooleanField 琳猫,但是 NULL 可作為其中一個選項(xiàng)。 |
PositiveIntegerField | NumberInput | 整數(shù)私痹。取值范圍是 0 到 2147483647 脐嫂,在 Django 支持的所有數(shù)據(jù)庫中可放心使用。 |
SlugField | TextInput | 別名(slug)是報業(yè)術(shù)語紊遵,是某個事物的簡短標(biāo)注雹锣,只包含字母、數(shù)字癞蚕、下劃線或連字符。 |
SmallIntegerField | NumberInput | 類似于 IntegerField 辉哥,但是對值有限制桦山。取值范圍是 -32768 到 32767 攒射,在 Django 支持的所有數(shù)據(jù)庫中可放心使用。 |
TextField | Textarea | 大段文本字段恒水。如果指定了 max_length 選項(xiàng)会放,這一限制在自動生成的表單字段中會體現(xiàn)出來。 |
TimeField | TextInput | 時間钉凌,在 Python 中使用 datetime.time 實(shí)例表示咧最。 |
URLField | URLInput | 用于輸入 URL 的 CharField ∮瘢可選 max_length 選項(xiàng)矢沿。 |
UUIDField | TextInput | 用于存儲通用唯一標(biāo)識碼。使用 Python 的 UUID 類酸纲。 |
文件和圖片上傳
文件上傳
- FileField 不支持 primary_key 和 unique 選項(xiàng)捣鲸,否則會拋出 TypeError
- FileField 實(shí)例對應(yīng)的是 varchar 列闽坡,最大長度默認(rèn)為 100 個字符〖残幔可以通過 max_length 修改
- 默認(rèn)的 HTML 中表單小組件是 ClearableFileInput
- 數(shù)據(jù)庫里不存儲文件,保存的是字符串(相對于 MEDIA_ROOT 的文件路徑)
- 查看圖片或者文件在數(shù)據(jù)庫中的路徑:
對象.字段名.url
參數(shù) upload_to
用于設(shè)置上傳地址的目錄和文件名汁蝶,示列代碼:
# 在 settings.py 中配置路徑
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 在 models.py 文件中定義模型
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
# workgerber 上傳到了 '/media/image/2018/10' 文件夾中
workgerber = models.FileField(upload_to="image/%Y/%m", verbose_name='生產(chǎn)gerber文件')
image = models.ImageField(upload_to="image/%Y/%m", default='image/default.png', verbose_name='用戶頭像')
upload_to
參數(shù)也可以接收一個回調(diào)函數(shù)次泽,回調(diào)函數(shù)接受兩個參數(shù):
- 實(shí)列穿仪,
FileField
所在模型的實(shí)例 - 文件名
如:
from django.db import models
from django.contrib.auth.models import User
# instance 是位置參數(shù),代指 Articles 實(shí)列本身
# 下列代碼中文件保存在 MEDIA_ROOT/uid_1/ 文件夾
# 實(shí)現(xiàn)了根據(jù)用戶 ID 來將文件分開保存
def user_directory_path(instance, filename):
return 'uid_{0}/{1}'.format(instance.user.id, filename)
class Articles(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
up_file = models.FileField(upload_to=user_directory_path)
圖片上傳
ImageFiel
是用于保存圖像的字段意荤,方法和屬性都繼承 FileField
, 增加了驗(yàn)證上傳的對象是否為有效的圖像啊片。增加了 height
和width
兩個屬性。需要 Pillow
庫支持玖像。
MEDIA URL 路徑設(shè)置
在 settings.py
中配置路徑
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
假如 models.py 中 ImageField 字段:
class MyModel(models.Model):
image = models.ImageField(upload_to="image/%Y/%m")
此時如果上傳 car.jpg 圖片紫谷,那么圖片在數(shù)據(jù)庫中保存的路徑為 image/2018/10/car.jpg。
如果想要在前端使用 MEDIA_URL 捐寥,需要在 settings.py > TEMPLATES > context_processors 中
添加 django.template.context_processors.media
笤昨,
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
# '''''''
'django.template.context_processors.media', # 添加在這里
],
},
},
]
然后就可以在前端模版中使用 MEDIA_URL:
<img class="ui image" src="{{ MEDIA_URL }}{{ content.image }}">
這樣后端更改 MEDIA_URL 路徑時,就不用改前端了握恳。
字段選項(xiàng)參數(shù)
通過字段選項(xiàng)瞒窒,可以實(shí)現(xiàn)對字段的約束。
選項(xiàng) | 說明 |
---|---|
null | 如果為 True乡洼,Django 將空值以 NULL 存儲到數(shù)據(jù)庫中崇裁,默認(rèn)值是 False匕坯, null 是數(shù)據(jù)庫范疇的概念。 |
blank | 如果為 True拔稳,則該字段允許為空白葛峻,默認(rèn)值是 False, blank 是表單驗(yàn)證證范疇的巴比。 |
choices | 可迭代的對象(如列表或元組)术奖,由兩個元組(包括自身)組成的可迭代對象構(gòu)成(如 [(A, B), (A, B) …]),用于設(shè)定字段的選項(xiàng)采记。如果設(shè)定這個選項(xiàng)挺庞,默認(rèn)的表單小組件將由標(biāo)準(zhǔn)的文本字段變成帶選項(xiàng)的選擇框选侨。各元組中的第一個元素是真正在模型上設(shè)定的值,第二個元素是人類可讀的名稱晨仑。 |
db_column | 字段使用的數(shù)據(jù)庫列名稱洪己。如未指定答捕,Django 將使用字段的名稱。 |
db_index | 設(shè)為 True 時沃琅,在字段上建立數(shù)據(jù)庫索引益眉。 |
default | 字段的默認(rèn)值 |
editable | 設(shè)為 False 時呜叫,字段不在管理后臺或其他 ModelForm 中顯示。驗(yàn)證模型時也會跳過娱颊。默認(rèn)為 True箱硕。 |
error_messages | 用于覆蓋字段拋出異常時的默認(rèn)消息剧罩。值為一個字典,通過鍵指定想覆蓋的錯誤消息镇防。錯誤消息鍵包括 null来氧、blank、invalid凫碌、invalid_choice僚楞、unique 和 unique_for_date泉褐。 |
primary_key | 設(shè)為 True 時,指定字段為模型的主鍵跳座。 |
unique | 設(shè)為 True 時疲眷,在表中字段的值必須是唯一的狂丝。除了 ManyToManyField倍试、OneToOneField 和 FileField 之外县习,其他字段都可以設(shè)定這個選項(xiàng)躁愿。 |
verbose_name | 字段的人類可讀名稱。如果未設(shè)定样勃,Django 將使用字段的屬性名稱(下劃線轉(zhuǎn)換成空格)自動生成一個峡眶。 |
關(guān)系
多對一 (ForeignKey)
多對一關(guān)系,需要兩個位置參數(shù)狮暑,一個是關(guān)聯(lián)的模型搬男,另一個是 on_delete
選項(xiàng)缔逛,外鍵要定義在多的一方按脚,如:
from django.db import models
# 一個作者可以有多篇文章
class Articles(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
要創(chuàng)建一個遞歸的外鍵,即一個對象和自身的多對一關(guān)系伞辛,如:
class Comment(models.Model):
title = models.CharField(max_length=64)
body = models.CharField(max_length=256)
# 一個評論下面可以有多個評論
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE,null=True, blank=True)
多對一(ForeignKey)的參數(shù)
ForeignKey.on_delete
當(dāng)外鍵關(guān)聯(lián)的對象被刪除時踊兜,Django 將模仿 on_delete 執(zhí)行相應(yīng)的操作,如:
# 當(dāng)刪除作者時垫言,不刪除對應(yīng)的文章,將作者設(shè)置為 null
author = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
- CASCADE :模擬 SQL 語言中的 ON DELETE CASCADE 約束筷频,將定義有外鍵的模型對象同時刪除。
- PROTECT : 阻止上面的刪除操作坯癣,但是彈出 ProtectedError 異常示罗。
- SET_NULL :將外鍵字段設(shè)為 null蚜点,只有當(dāng)字段設(shè)置了 null=True 時,方可使用該值。
- SET_DEFAULT : 將外鍵字段設(shè)為默認(rèn)值实辑。只有當(dāng)字段設(shè)置了 default 參數(shù)時,方可使用悠反。
- DO_NOTHING :什么也不做斋否。
- SET() :設(shè)置為一個傳遞給 SET() 的值或者一個回調(diào)函數(shù)的返回值疫诽。
ForeignKey.limit_choices_to
限制外鍵所能關(guān)聯(lián)的對象奇徒,參數(shù)只能在 ModelForm
和 admin
后臺使用,值可以是一個字典胖笛、Q 對象或者一個返回字典或 Q 對象的函數(shù)調(diào)用匀钧,如:
staff_member = models.ForeignKey(
User,
on_delete=models.CASCADE,
limit_choices_to={'is_staff': True},
)
# 在 ModelForm 的 staff_member 字段列表中之斯,只會出現(xiàn)那些 is_staff=True 的 Users 對象
參考下面的方式,使用函數(shù)調(diào)用:
def limit_pub_date_choices():
return {'pub_date__lte': datetime.date.utcnow()}
# 把可選對象限定到 pub_date 早于當(dāng)前時間的對象中
limit_choices_to = limit_pub_date_choices
ForeignKey.related_name
反向操作時瘫絮,使用的字段名麦萤,用于代替 '表名_set' 如: object. 表名_set.all()
, 也是 related_query_name(目標(biāo)模型使用的反向過濾器名稱)的默認(rèn)值壮莹,如:
ForeignKey.related_query_name
反向操作時涝滴,使用的連接前綴歼疮,用于替換'表名' 如: models.UserGroup.objects.filter(表名__字段名稱 =1)
ForeignKey.to_field
默認(rèn)情況下,外鍵都是關(guān)聯(lián)到被關(guān)聯(lián)對象的主鍵上骤素。如果指定這個參數(shù),可以關(guān)聯(lián)到關(guān)聯(lián)表的指定字段上霎槐,但是該字段必須具有 unique=True
屬性。
ForeignKey.db_constraint
決定是否在數(shù)據(jù)庫中為這個外鍵創(chuàng)建約束. 默認(rèn)值為 True
ForeignKey.swappable
控制外鍵指向可交換的模型時遷移框架的反應(yīng),默認(rèn)值為 True
多對多 (ManyToManyField)
多對多有一個必須的位置參數(shù)唁桩,關(guān)聯(lián)的對象模型荒澡。
class Tags(models.Model):
name = models.CharField(max_length=32)
class Articles(models.Model):
title = models.CharField(max_length=32)
tag = models.ManyToManyField(Tags, blank=True)
多對多除了生成各自的表以外碍现,還會生成第三張表昼接,用來管理雙方的關(guān)系慢睡,可以用 db_table 選項(xiàng)設(shè)定泪喊。
多對多 (ManyToManyField) 的參數(shù)
ManyToManyField.related_name
同 ForeignKey.related_name窘俺。
ManyToManyField.related_query_name
同 ForeignKey.related_query_name。
ManyToManyField.limit_choices_to
同 ForeignKey.limit_choices_to。如果通過 through 參數(shù)自定義了中間聯(lián)結(jié)表髓棋,ManyToManyField 的 limit_choices_to 參數(shù)沒有作用膳犹。
ManyToManyField.symmetrical
只用于與自身進(jìn)行關(guān)聯(lián)的ManyToManyField. 例如下面的模型
from django.db import models
class Person(models.Model):
friends = models.ManyToManyField("self")
默認(rèn)的情況下,django
中多對多關(guān)系是對稱的豺旬,django 不會為 Person
類添加 person_set
屬性用于反向關(guān)聯(lián)族阅,如果不需要對稱關(guān)系可以將symmetrical
設(shè)置為 False
,強(qiáng)制 Django 為反向關(guān)聯(lián)添加描述符
ManyToManyField.through
Django 會自動生成一個表求泰,用于管理多對多關(guān)系渴频。如果想自定義中間表拔第,可以通過 through 選項(xiàng)指定表示中間表的 Django 模型蚊俺。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership', # 自定義中間表 ‘Membership’
through_fields=('group', 'person'),
)
# 定義中間表的模型
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
# 增加邀請人
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
# 增加進(jìn)入時間
date_joined = models.DateField()
# 增加邀請理由
invite_reason = models.CharField(max_length=64)
ManyToManyField.through_fields
上面的例子中。Membership 模型中包含三個關(guān)聯(lián) Person 的外鍵,Django 無法確定到底使用哪個作為和 Group 關(guān)聯(lián)的對象忙上。所以疫粥,必須顯式的指定 through_fields
參數(shù),用于定義關(guān)系库糠。
through_fields
參數(shù)接受一個元組('field1', 'field2') field1 指向定義多對多模型的外鍵字段名稱(Membership
模型中的 group
),field2 指向目標(biāo)模型 的外鍵字段名稱()(Membership
模型中的 person
)贷屎。
如果中間表中只有兩個外鍵的話罢防,可以不用指定 through_fields
,django一般可以自動識別。
ManyToManyField.db_table
設(shè)置中間表的名稱唉侄,不指定的話使用默認(rèn)值
ManyToManyField.db_constraint
同 ForeignKey.db_constraint
ManyToManyField.swappable
同 ForeignKey.swappable
一對一(OneToOneField)
一對一,在概念上, 它類似于設(shè)置了 unique=True
的 ForeignKey
, 但是咒吐,一對一關(guān)系的反向關(guān)聯(lián)的對象只有一個
如果沒有為 OneToOneField 指定 related_name
參數(shù),Django 使用當(dāng)前模型的小寫作為默認(rèn)值:
from django.conf import settings
from django.db import models
class MySpecialUser(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL属划,on_delete=models.CASCADE)
supervisor = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='supervisor_of'恬叹, on_delete=models.CASCADE)
'User' 模型便具有了以下屬性:
>>> user = User.objects.get(pk=1)
>>> hasattr(user, 'myspecialuser')
True
>>> hasattr(user, 'supervisor_of')
True
一對一(OneToOneField)參數(shù)
OneToOneField 接受的參數(shù)與 ForeignKey 完全一樣,此外還有一個 parent_link
參數(shù)目溉。
管理器
- Django 模型至少有一個管理器脸狸,可以自定義管理器,定制訪問數(shù)據(jù)庫的方式
- 模型的管理器是 Django 模型用于執(zhí)行數(shù)據(jù)庫查詢的對象
- 當(dāng)定義模型類時沒有指定管理器媚值,則 Django 會為模型類提供一個名為 objects 的管理器
- 自定義管理器可能出于兩方面的原因:添加額外的管理器方法和(或)修改管理器返回的 QuerySet。
- 添加額外的管理器方法是為了給模型添加 數(shù)據(jù)表 層功能
添加額外的管理器方法
當(dāng)不滿足默認(rèn)的管理器方法時,可以給管理器添加額外的方法甩苛,新的管理器需要繼承 django.db.models.Manager
, 在 model 中將 新管理器 賦值給 objects
即可
class BookManager(models.Manager):
def title_count(self, keyword):
return self.filter(title__icontains=keyword).count()
# BookManager 類擴(kuò)展 django.db.models.Manager
# self 代指管理器本身
class Book(models.Model):
title = models.CharField(max_length=128)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
publication_data = models.DateField(blank=True, null=True)
objects = BookManager()
# BookManager() 賦值給模型的objects 屬性
# 將BookManager()替換“默認(rèn)”管理器
def __str__(self):
return self.title
如此反浓,objects 出來的原來的方法外,還多了一個 title_count()
方法,如下調(diào)用:
In [4]: Book.objects.title_count('ava')
Out[4]: 1
In [6]: Book.objects.all()
Out[6]: <QuerySet [<Book: Python3 Cook Book>, <Book: Java>]># BookManager()除了有title_count方法外拴念,也有默認(rèn)的objects其它方法
修改管理器返回的查詢集合(QuerySet)
- 如果想修改管理器返回的 QuerySet,需要覆蓋
Manager.get_queryset()
方法昧谊。get_queryset()
方法需要返回一個 Query-Set - Django 遇到的第一個管理器(按照在模型中定義的順序)定義為“默認(rèn)的”管理器草姻。
- 同一個模型上可以使用多個管理器
class MaleManager(models.Manager):
def get_queryset(self):
return super(MaleManager, self).get_queryset().filter(gender='Male')
class FemaleManager(models.Manager):
def get_queryset(self):
return super(FemaleManager, self).get_queryset().filter(gender='Female')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
gender = models.CharField(max_length=6,
choices=(
('Male', '男'),
('Female', '女')
)
)
people = models.Manager() # 默認(rèn)管理器,選擇全部對象
men = MaleManager() # 只選擇 gender = Male 的對象
women = FemaleManager() # 只選擇 gender = Female 的對象
模型方法
模型中自定義的方法為對象添加數(shù)據(jù)行層的功能谣妻。管理器的作用是執(zhí)行數(shù)據(jù)表層的操作减江,而模型方法處理的是具體的模型實(shí)例茵休。
自定義模型方法
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):
# 返回一個人的出生日期與嬰兒潮的關(guān)系
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):
# 返回一個人的全名
return '%s %s' % (self.first_name, self.last_name)
# 使用 property 函數(shù)將類方法包裝為屬性
full_name = property(_get_full_name)
重寫預(yù)定義的模型方法
如果想在調(diào)用 save()
等預(yù)定義方法之前做些什么的話唠雕,可以重寫這些方法阎毅,如:
# save() 將模型對象保存到數(shù)據(jù)表中
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) # 調(diào)用 “真正的” save() 方法
do_something_else()
需要在方法中繼承超類方法 super(Blog, self).save(*args, **kwargs)
,否則不會保存到數(shù)據(jù)庫
Meta選項(xiàng)
使用 class Meta:
在模型中增加一個子類,如
模型元數(shù)據(jù)選項(xiàng)
選項(xiàng) | 說明 |
---|---|
abstract | 設(shè)為 True 時表明模型是抽象基類。 |
app_label | 如果定義模型的應(yīng)用不在 INSTALLED_APPS 中赂韵,必須指定所屬的應(yīng)用瞬测。 |
db_table | 模型使用的數(shù)據(jù)庫表名稱。 |
db_tablespace | 模型使用的數(shù)據(jù)庫表空間。默認(rèn)為項(xiàng)目的 DEFAULT_TABLESPACE 設(shè)置(如果設(shè)定了)。如果數(shù)據(jù)庫后端不支持表空間,忽略這個選項(xiàng)。 |
default_related_name | 關(guān)聯(lián)的對象回指這個模型默認(rèn)使用的名稱。默認(rèn)為<model_name>_set。 |
get_latest_by | 模型中可排序字段的名稱,通常是一個 DateField、DateTimeField 或 IntegerField。 |
managed | 默認(rèn)為 True俯艰,即讓 Django 在遷移中創(chuàng)建適當(dāng)?shù)臄?shù)據(jù)庫表啦辐,并在執(zhí)行 flush 管理命令時把表刪除跑芳。 |
order_with_respect_to | 標(biāo)記對象為可排序的,排序依據(jù)是指定的字段。 |
ordering | 對象的默認(rèn)排序疆液,獲取對象列表時使用。 |
permissions | 創(chuàng)建對象時寫入權(quán)限表的額外權(quán)限陕贮。 |
default_permissions | 默認(rèn)為 ('add', 'change', 'delete')堕油。 |
proxy | 設(shè)為 True 時,定義為另一個模型的子類的模型視作代理模型肮之。 |
select_on_save | 指明是否讓 Django 使用 1.6 版之前的 django.db.models.Model.save() 算法馍迄。 |
unique_together | 設(shè)定組合在一起時必須唯一的多個字段名稱。 |
index_together | 設(shè)定在一起建立索引的多個字段名稱局骤。 |
verbose_name | 為對象設(shè)定人類可讀的名稱(單數(shù))攀圈。 |
verbose_name_plural | 設(shè)定對象的復(fù)數(shù)名稱。 |
模型繼承
Django 的模型和 Python 的類一樣峦甩,支持繼承赘来,有 3 種繼承方式:
- 抽象基類
- 多表繼承
- 代理模型
抽象基類
基類模型不會創(chuàng)建數(shù)據(jù)表现喳,在模型中的 Meta 類中添加 abstract=True
將普通模型轉(zhuǎn)換為抽象基類。子模型會擁有基類模型中的全部字段犬辰。如:
from django.db import models
class BaseModel(models.Model):
name = models.CharField(max_length=32)
thickness = models.CharField(max_length=6)
length = models.CharField(max_length=12)
height = models.CharField(max_length=12)
create_time = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class CustomerBoard(BaseModel):
customer_code = models.CharField(max_length=12)
def __str__(self):
return '{}_{}'.format(self.customer_code, self.name)
上面的 CustomerBoard 模型擁有抽象基類 BaseModel 中所有的字段嗦篱,如:
In[5]: board = CustomerBoard.objects.get(pk=1)
In[6]: board.name
Out[6]: 'first board'
In[7]: board.thickness
Out[7]: '1.0mm'
In[8]: board.create_time
Out[8]: datetime.datetime(2018, 10, 11, 13, 1, 2, 190123, tzinfo=<UTC>)
抽象基類的 Meta 類
- 當(dāng)子類沒有定義自己的 Meta 類,子類將會繼承抽象基類的 Meta 類幌缝。
- 當(dāng)抽象基類有的元數(shù)據(jù)灸促,在子類沒有的話,直接繼承沒有的元數(shù)據(jù)涵卵。
- 當(dāng)抽象基類有的元數(shù)據(jù)浴栽,在子類同樣有的話,直接覆蓋基類的元數(shù)據(jù)轿偎。
- 子類可以先繼承抽象基類中的 Meta 元數(shù)據(jù)典鸡,然后再定義自己的 Meta 類添加額外的元數(shù)據(jù)。
- 在子模型中 Meta 中添加
abstract = True
坏晦,可以把子類變成抽象基類
抽象基類中的 related_name
和 related_query_name
ForeignKey
或 ManyToManyField
字段上使用的 related_name
和 related_query_name
屬性時萝玷,需要制定唯一的反向名稱,但是如果在抽象基類這樣做的話昆婿,會出現(xiàn)所有繼承該抽象基類的子模型 related_name
和 related_query_name
完全相同球碉,可以使用 '%(app_label)s' 和 '%(class)s'字符串來解決
- '%(app_label)s' 會被子類所在的 APP 的名字取代
- '%(class)s' 會被子類的名字取代
如,在 app common 中仓蛆,common/models.py:
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(
OtherModel,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",
)
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
上面的繼承關(guān)系中:
- common.ChildA.m2m 字段的反向名稱為 common_childa_related
- common.ChildB.m2m 字段的反向名稱為 common_childb_related
- rare app 中 rare.ChildB.m2m 字段的反向名稱為 rare_childb_related
如果沒有在抽象基類中定義 related_name
屬性,反向名稱就是 小寫子模型名稱_set
多律。
多表繼承
多表繼承中痴突,父類和子類都會創(chuàng)建數(shù)據(jù)庫,繼承關(guān)系是通過子模型和它每個父類添加一個自動創(chuàng)建的 OneToOneField 鏈接來實(shí)現(xiàn)狼荞,如:
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
中都是可以使用的辽装,但是字段不會保存在 Restaurant
中,如:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
如果一個 Place 也是一個 Restaurant相味,可以使用 Place對象.restaurant
取到對應(yīng)的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
只是單純的 Place
那 p.restaurant
會拋出 Restaurant.DoesNotExist
多表繼承中的Meta類型
除了 ordering
和 get_latest_by
兩個元數(shù)據(jù)外拾积,其余的元數(shù)據(jù)都不會被子類繼承。
如果不想繼承父類的 ordering
和 get_latest_by
參數(shù)丰涉,需要在之類中重寫或者禁用:
class ChildModel(ParentModel):
# ...
class Meta:
# 清空排序
ordering = []
代理繼承
代理模型的作用:
- 可以創(chuàng)建拓巧、刪除、更新代理模型的實(shí)例一死,并且所有的數(shù)據(jù)都可以像使用原始模型(非代理類模型)一樣被保存肛度。
- 可以在代理模型中改變默認(rèn)的排序方式和默認(rèn)的 manager 管理器 等等,而不會對原始模型產(chǎn)生影響投慈。
聲明一個代理模型只需要將 Meta
中 proxy
的值設(shè)為 True
承耿。如:
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 操作同一個數(shù)據(jù)表冠骄。Person 的任何實(shí)例都可以通過 MyPerson訪問,反之亦然:
>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
也可以讓父類模型正常查詢加袋,而代理排序凛辣,如:
class OrderedPerson(Person):
class Meta:
ordering = ["last_name"]
proxy = True
現(xiàn)在,普通的Person查詢是無序的职烧,而 OrderedPerson查詢會按照last_name排序扁誓。
基類的限制
- 代理模型必須繼承自一個非抽象基類。 并且不能繼承自多個非抽象基類
- 代理模型可以繼承任意多個抽象基類蚀之,但前提是它們沒有定義任何
model
字段 - 代理模型可以同時繼承多個別的代理模型蝗敢,但前提是這些代理模型繼承同一個非抽象基類
代理模型的管理器
如果代理模型中沒有定義管理器,代理模型就會從父類中繼承管理器恬总。如果代理模型中定義了管理器,它就會變成默認(rèn)的管理器肚邢,不過定義在父類中的管理器仍然有效壹堰。
from django.db import models
class NewManager(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
只是需要添加管理器而不是替換默認(rèn)管理器時,可以創(chuàng)建一個含有新的管理器的基類骡湖,并且在繼承時把他放在主基類的后面:
# 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