Django 中內(nèi)嵌的 ORM 模型
本小節(jié)將詳細(xì)為大家介紹 Django 中內(nèi)嵌的 ORM 模型及其使用,這里我會(huì)結(jié)合源碼的方式為大家展示 Django 內(nèi)部 ORM 模型的實(shí)現(xiàn)原理。
1. ORM 介紹
ORM 的概念如下:
對(duì)象關(guān)系映射(Object Relational Mapping,簡(jiǎn)稱(chēng)ORM)模式是一種為了解決面向?qū)ο笈c關(guān)系數(shù)據(jù)庫(kù)存在的互不匹配的現(xiàn)象的技術(shù)。
簡(jiǎn)單的說(shuō),ORM 是通過(guò)使用描述對(duì)象和數(shù)據(jù)庫(kù)之間映射的元數(shù)據(jù),將程序中的對(duì)象自動(dòng)持久化到關(guān)系數(shù)據(jù)庫(kù)中。ORM 在業(yè)務(wù)邏輯層和數(shù)據(jù)庫(kù)層之間充當(dāng)了橋梁的作用。ORM 解決的主要問(wèn)題是對(duì)象和關(guān)系的映射。它通常把一個(gè)類(lèi)和一個(gè)表一一對(duì)應(yīng),類(lèi)的每個(gè)實(shí)例對(duì)應(yīng)表中的一條記錄,類(lèi)的每個(gè)屬性對(duì)應(yīng)表中的每個(gè)字段,具體如下圖所示。ORM 提供了對(duì)數(shù)據(jù)庫(kù)的映射,不用直接編寫(xiě) SQL 代碼,只需像操作對(duì)象一樣從數(shù)據(jù)庫(kù)操作數(shù)據(jù)。讓軟件開(kāi)發(fā)人員專(zhuān)注于業(yè)務(wù)邏輯的處理,提高了開(kāi)發(fā)效率。
ORM 模式也是有一定缺點(diǎn)的,它會(huì)在一定程度上犧牲程序的執(zhí)行效率。此外,還存在許多復(fù)雜場(chǎng)景是 ORM 模式無(wú)法解決的,同樣還是需要手動(dòng)編寫(xiě) SQL 語(yǔ)句完成。
2. Django 內(nèi)嵌的 ORM 模型
2.1 Django 中的模型說(shuō)明
在 Django 中,一個(gè)模型(model)會(huì)映射到一個(gè)數(shù)據(jù)庫(kù)表。每個(gè)模型都是一個(gè)Python 類(lèi),它是django.db.models.Model 的子類(lèi),模型的每個(gè)屬性都代表一個(gè)數(shù)據(jù)庫(kù)字段。例如下面的代碼中,我們定義了一個(gè) Member 類(lèi)。每個(gè) model 會(huì)屬于 Django 中的一個(gè)應(yīng)用,我們通常會(huì)將每個(gè)應(yīng)用的 models 寫(xiě)到該應(yīng)用目錄下的 models.py
中。
# first_django_app/hello_app/models.py
from django.db import models
class Member(models.Model):
sex_choices = (
(0, '男'),
(1, '女'),
)
name = models.CharField('姓名', max_length=30)
age = models.CharField('年齡', max_length=30)
sex = models.SmallIntegerField('性別', choices=sex_choices, default=0)
occupation = models.CharField('職業(yè)', max_length=30)
phone_num = models.CharField('手機(jī)號(hào)', max_length=14, null=True)
email = models.EmailField('郵箱', blank=True)
city = models.CharField('城市', max_length=30)
register_date = models.DateTimeField('注冊(cè)時(shí)間', auto_now=True)
def __str__(self):
return "<%s, %s>" % (self.name, self.phone_num)
class Meta:
# 通過(guò)db_table自定義數(shù)據(jù)表名
db_table = 'member'
上面模型類(lèi)的定義中,我們看到幾個(gè)和模型相關(guān)的字段類(lèi),比如 CharField、SmallIntegerField 等。Django 中定義了許多類(lèi)似的字段類(lèi),這些字段類(lèi)和數(shù)據(jù)庫(kù)中字段的類(lèi)型是一一映射的。以 MySQL 為例:
# 源碼地址 django/db/backends/mysql/base.py
# ...
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'mysql'
display_name = 'MySQL'
# This dictionary maps Field objects to their associated MySQL column
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
data_types = {
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime(6)',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time(6)',
'UUIDField': 'char(32)',
}
# ...
# ...
從上面這部分源碼可以看到,Django 中定義的這些字段類(lèi)都會(huì)和 MySQL 中的字段的類(lèi)型是一一對(duì)應(yīng)的。接下來(lái)介紹常用的 Field 類(lèi)型以及相關(guān)的屬性選項(xiàng)。
2.2 Django 中常用的 Field types
在 Django 模型中,每個(gè)字段都應(yīng)該是相應(yīng) Field 類(lèi)的實(shí)例,以此決定該表在數(shù)據(jù)庫(kù)中保存字段的數(shù)據(jù)類(lèi)型。在上面的源碼中我們看到 Django 內(nèi)部是定義了25個(gè)字段類(lèi),其中常用的 Field 類(lèi)型如下:
-
AutoField:int 自增列,必須填入?yún)?shù) primary_key=True。當(dāng) model 中如果沒(méi)有自增列,則自動(dòng)會(huì)創(chuàng)建一個(gè)列名為 id 的列;
-
BooleanField:布爾類(lèi)型 (True/False),這個(gè)Field不接受null參數(shù),要想使用可以為 null 的布爾類(lèi)型的字段,就要使用 NullBooleanField;
-
CharField:最常用的字段類(lèi),映射到數(shù)據(jù)庫(kù)中會(huì)轉(zhuǎn)換成 varchar 類(lèi)型,使用時(shí)必須傳入 max_length 屬性以定義該字符串的最大長(zhǎng)度,如果超過(guò)254個(gè)字符,就不建議使用 CharField 了,此時(shí)建議使用 TextField;
-
DateField 和 DateTimeField:都是日期時(shí)間的字段類(lèi),注意前者只到天,后者可以精確到毫秒。使用這兩個(gè) Field 可以傳遞以下幾個(gè)參數(shù):
- auto_now=True:在每次這個(gè)數(shù)據(jù)保存的時(shí)候,都使用當(dāng)前的時(shí)間;
- auto_now_add=True:在每條數(shù)據(jù)第一次被添加進(jìn)去的時(shí)候,都使用當(dāng)前的時(shí)間;
此外要注意的是 auto_add_now,auto_now 與 default 是互斥的。
-
DecimalField:處理浮點(diǎn)類(lèi)型的 Field。從上面的源碼可以看到,它有兩個(gè)必須填入的參數(shù):
-
max_digits:數(shù)字允許的最大位數(shù);
-
decimal_places:小數(shù)的最大位數(shù);
-
-
FloatField:也是處理浮點(diǎn)類(lèi)型的 Field。它和 DecimalField 的區(qū)別就是 Python 中 float 和 decimal 的區(qū)別;
-
IntegerField /BigIntegerField/SmallIntegerField:都是處理整數(shù)類(lèi)型的 Field;
-
TextField:長(zhǎng)文本類(lèi)型 Field,對(duì)應(yīng) MySQL 中的 longtext 類(lèi)型。
2.3 Django 中的 Field options
每種 Field 類(lèi)會(huì)有一些特定的 Field 選項(xiàng),比如 CharField 必須要傳入 max_length 屬性值。但是下面這些屬性對(duì)于所有 Field 類(lèi)都是有效的:
- null:默認(rèn)為 False。如果為 True 則表明在數(shù)據(jù)庫(kù)中該字段可以為 null;
- blank:默認(rèn)為 False。如果為 True 則表明在數(shù)據(jù)庫(kù)中該字段可以為不填;
- choice:設(shè)置可選項(xiàng),表明該字段的值只能從 choice 中選擇,例如上面 Member 表中定義的 sex 字段,只能為 0 或者 1,代表的含義分別為男或者女;
- default:設(shè)置字段的默認(rèn)值;
- help_text:設(shè)置說(shuō)明信息;
- primary_key:如果為 True,表明設(shè)置該字段為主鍵。此時(shí) Django 便不會(huì)再為我們添加默認(rèn)的 id 主鍵了;
- unique:設(shè)置該字段的值在表中唯一。
2.4 數(shù)據(jù)庫(kù)中生成模型表
接下來(lái),我們需要使用 Django 給我們提供的兩個(gè)命令來(lái)在數(shù)據(jù)庫(kù)中生成 hello_app 應(yīng)用下定義的數(shù)據(jù)模型。注意: Member 類(lèi)映射的表名默認(rèn)是【應(yīng)用名_類(lèi)名小寫(xiě)】,然而在前面的模型代碼中我們通過(guò) model 的 Meta 類(lèi)中的 db_table 參數(shù)改寫(xiě)了數(shù)據(jù)庫(kù)的具體名稱(chēng),所以最后數(shù)據(jù)庫(kù)中生成的表名為 member,而不是 hello_app_member。
(django-manual) [root@server first_django_app]# python manage.py makemigrations hello_app
Migrations for 'hello_app':
hello_app/migrations/0001_initial.py
- Create model Member
(django-manual) [root@server first_django_app]# python manage.py migrate hello_app
Operations to perform:
Apply all migrations: hello_app
Running migrations:
Applying hello_app.0001_initial... OK
執(zhí)行完成后,此時(shí) hello_app 應(yīng)用下的所有 model 就會(huì)被映射到 MySQL 數(shù)據(jù)庫(kù)中,且會(huì)對(duì)應(yīng)生成相應(yīng)的模型表(此外還有一個(gè)遷移記錄表 django_migrations):
MySQL [django_manual]> show tables;
+-------------------------+
| Tables_in_django_manual |
+-------------------------+
| django_migrations |
| member |
| user |
+-------------------------+
3 rows in set (0.00 sec)
我們還可以通過(guò) show create table 表名
命令顯示表的創(chuàng)建語(yǔ)句:
MySQL [django_manual]> show create table member;
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| member | CREATE TABLE `member` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
`age` varchar(30) NOT NULL,
`sex` smallint(6) NOT NULL,
`occupation` varchar(30) NOT NULL,
`phone_num` varchar(14) NOT NULL,
`email` varchar(254) NOT NULL,
`city` varchar(30) NOT NULL,
`register_date` datetime(6) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
3. 小結(jié)
本小結(jié)中我們介紹了 ORM 的基本概念,然后講解了 Django 中的 model 相關(guān)知識(shí),介紹了模型層中常見(jiàn)的字段類(lèi)型和字段選項(xiàng)。最后實(shí)戰(zhàn)演示了如何通過(guò) Django 提供的命令在數(shù)據(jù)庫(kù)中生成模型層定義的表。接下來(lái)我們會(huì)在生成的表中使用 Django 給我們提供的 ORM 模型對(duì)表進(jìn)行增刪改查操作。