17 Django 中內嵌的 ORM 模型
本小節將詳細為大家介紹 Django 中內嵌的 ORM 模型及其使用,這裡我會結合原始碼的方式為大家展示 Django 內部 ORM 模型的實現原理。
1. ORM 介紹
ORM 的概念如下:
物件關係對映(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向物件與關係資料庫存在的互不匹配的現象的技術。
簡單的說,ORM 是通過使用描述物件和資料庫之間對映的元資料,將程式中的物件自動持久化到關係資料庫中。ORM 在業務邏輯層和資料庫層之間充當了橋樑的作用。ORM 解決的主要問題是物件和關係的對映。它通常把一個類和一個表一一對應,類的每個例項對應表中的一條記錄,類的每個屬性對應表中的每個欄位
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 模型對錶進行增刪改查操作。