Django 中內嵌的 ORM 模型
本小節將詳細為大家介紹 Django 中內嵌的 ORM 模型及其使用,這里我會結合源碼的方式為大家展示 Django 內部 ORM 模型的實現原理。
1. ORM 介紹
ORM 的概念如下:
對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系數據庫存在的互不匹配的現象的技術。
簡單的說,ORM 是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關系數據庫中。ORM 在業務邏輯層和數據庫層之間充當了橋梁的作用。ORM 解決的主要問題是對象和關系的映射。它通常把一個類和一個表一一對應,類的每個實例對應表中的一條記錄,類的每個屬性對應表中的每個字段,具體如下圖所示。ORM 提供了對數據庫的映射,不用直接編寫 SQL 代碼,只需像操作對象一樣從數據庫操作數據。讓軟件開發人員專注于業務邏輯的處理,提高了開發效率。
ORM 模式也是有一定缺點的,它會在一定程度上犧牲程序的執行效率。此外,還存在許多復雜場景是 ORM 模式無法解決的,同樣還是需要手動編寫 SQL 語句完成。
2. Django 內嵌的 ORM 模型
2.1 Django 中的模型說明
在 Django 中,一個模型(model)會映射到一個數據庫表。每個模型都是一個Python 類,它是django.db.models.Model 的子類,模型的每個屬性都代表一個數據庫字段。例如下面的代碼中,我們定義了一個 Member 類。每個 model 會屬于 Django 中的一個應用,我們通常會將每個應用的 models 寫到該應用目錄下的 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('職業', max_length=30)
phone_num = models.CharField('手機號', max_length=14, null=True)
email = models.EmailField('郵箱', blank=True)
city = models.CharField('城市', max_length=30)
register_date = models.DateTimeField('注冊時間', auto_now=True)
def __str__(self):
return "<%s, %s>" % (self.name, self.phone_num)
class Meta:
# 通過db_table自定義數據表名
db_table = 'member'
上面模型類的定義中,我們看到幾個和模型相關的字段類,比如 CharField、SmallIntegerField 等。Django 中定義了許多類似的字段類,這些字段類和數據庫中字段的類型是一一映射的。以 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 中定義的這些字段類都會和 MySQL 中的字段的類型是一一對應的。接下來介紹常用的 Field 類型以及相關的屬性選項。
2.2 Django 中常用的 Field types
在 Django 模型中,每個字段都應該是相應 Field 類的實例,以此決定該表在數據庫中保存字段的數據類型。在上面的源碼中我們看到 Django 內部是定義了25個字段類,其中常用的 Field 類型如下:
-
AutoField:int 自增列,必須填入參數 primary_key=True。當 model 中如果沒有自增列,則自動會創建一個列名為 id 的列;
-
BooleanField:布爾類型 (True/False),這個Field不接受null參數,要想使用可以為 null 的布爾類型的字段,就要使用 NullBooleanField;
-
CharField:最常用的字段類,映射到數據庫中會轉換成 varchar 類型,使用時必須傳入 max_length 屬性以定義該字符串的最大長度,如果超過254個字符,就不建議使用 CharField 了,此時建議使用 TextField;
-
DateField 和 DateTimeField:都是日期時間的字段類,注意前者只到天,后者可以精確到毫秒。使用這兩個 Field 可以傳遞以下幾個參數:
- auto_now=True:在每次這個數據保存的時候,都使用當前的時間;
- auto_now_add=True:在每條數據第一次被添加進去的時候,都使用當前的時間;
此外要注意的是 auto_add_now,auto_now 與 default 是互斥的。
-
DecimalField:處理浮點類型的 Field。從上面的源碼可以看到,它有兩個必須填入的參數:
-
max_digits:數字允許的最大位數;
-
decimal_places:小數的最大位數;
-
-
FloatField:也是處理浮點類型的 Field。它和 DecimalField 的區別就是 Python 中 float 和 decimal 的區別;
-
IntegerField /BigIntegerField/SmallIntegerField:都是處理整數類型的 Field;
-
TextField:長文本類型 Field,對應 MySQL 中的 longtext 類型。
2.3 Django 中的 Field options
每種 Field 類會有一些特定的 Field 選項,比如 CharField 必須要傳入 max_length 屬性值。但是下面這些屬性對于所有 Field 類都是有效的:
- null:默認為 False。如果為 True 則表明在數據庫中該字段可以為 null;
- blank:默認為 False。如果為 True 則表明在數據庫中該字段可以為不填;
- choice:設置可選項,表明該字段的值只能從 choice 中選擇,例如上面 Member 表中定義的 sex 字段,只能為 0 或者 1,代表的含義分別為男或者女;
- default:設置字段的默認值;
- help_text:設置說明信息;
- primary_key:如果為 True,表明設置該字段為主鍵。此時 Django 便不會再為我們添加默認的 id 主鍵了;
- unique:設置該字段的值在表中唯一。
2.4 數據庫中生成模型表
接下來,我們需要使用 Django 給我們提供的兩個命令來在數據庫中生成 hello_app 應用下定義的數據模型。注意: Member 類映射的表名默認是【應用名_類名小寫】,然而在前面的模型代碼中我們通過 model 的 Meta 類中的 db_table 參數改寫了數據庫的具體名稱,所以最后數據庫中生成的表名為 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
執行完成后,此時 hello_app 應用下的所有 model 就會被映射到 MySQL 數據庫中,且會對應生成相應的模型表(此外還有一個遷移記錄表 django_migrations):
MySQL [django_manual]> show tables;
+-------------------------+
| Tables_in_django_manual |
+-------------------------+
| django_migrations |
| member |
| user |
+-------------------------+
3 rows in set (0.00 sec)
我們還可以通過 show create table 表名
命令顯示表的創建語句:
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. 小結
本小結中我們介紹了 ORM 的基本概念,然后講解了 Django 中的 model 相關知識,介紹了模型層中常見的字段類型和字段選項。最后實戰演示了如何通過 Django 提供的命令在數據庫中生成模型層定義的表。接下來我們會在生成的表中使用 Django 給我們提供的 ORM 模型對表進行增刪改查操作。