orm是什么
o是 object,也就 類對象 的意思,r是 relation,翻譯成中文是 關系,也就是關系數據庫中 數據表 的意思,m是 mapping,是映射的意思。在orm框架中,它幫我們把類和數據表進行了一個映射,可以讓我們通過類和類對象就能操作它所對應的表格中的數據。orm框架還有一個功能,它可以根據我們設計的類自動幫我們生成數據庫中的表,省去了我們自己建表的過程。
一個句話理解就是:創建一個實例對象,用創建它的類名當做數據表名,用創建它的類屬性對應數據表的字段,當對這個實例對象操作時,能夠對應 mysql 語句。
在 django 中就內嵌了一個 orm 框架,不需要直接面向數據庫編程,而是定義模型類,通過模型類和對象完成數據表的增刪改查操作。還有第三方庫 sqlalchemy 都是 orm框架。
先看看我們大致要實現什么功能
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class user(父類省略): uid = ( 'uid' , "int unsigned" ) name = ( 'username' , "varchar(30)" ) email = ( 'email' , "varchar(30)" ) password = ( 'password' , "varchar(30)" ) ...省略... user = user(uid = 123 , name = 'hui' , email = 'huidbk@163.com' , password = '123456' ) user.save() # 對應如下sql語句 # insert into user (uid,username,email,password) values (123,hui,huidbk@163.com,123456) |
所謂的 orm 就是讓開發者在操作數據庫的時候,能夠像操作對象時通過xxxx.屬性=yyyy一樣簡單,這是開發orm的初衷。
實現orm中的insert功能
通過 python 中 元類 簡單實現 orm 中的 insert 功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @author: hui # @desc: { 利用python元類簡單實現orm框架的insert插入功能 } # @date: 2021/05/17 17:02 class modelmetaclass( type ): """數據表模型元類""" def __new__(mcs, cls_name, bases, attrs): print (f 'cls_name -> {cls_name}' ) # 類名 print (f 'bases -> {bases}' ) # 繼承類 print (f 'attrs -> {attrs}' ) # 類中所有屬性 print () # 數據表對應關系字典 mappings = dict () # 過濾出對應數據表的字段屬性 for k, v in attrs.items(): # 判斷是否是指定的stringfield或者integerfield的實例對象 # 這里就簡單判斷字段是元組 if isinstance (v, tuple ): print ( 'found mapping: %s ==> %s' % (k, v)) mappings[k] = v # 刪除這些已經在字典中存儲的字段屬性 for k in mappings.keys(): attrs.pop(k) # 將之前的uid/name/email/password以及對應的對象引用、類名字 # 用其他類屬性名稱保存 attrs[ '__mappings__' ] = mappings # 保存屬性和列的映射關系 attrs[ '__table__' ] = cls_name # 假設表名和類名一致 return type .__new__(mcs, cls_name, bases, attrs) class user(metaclass = modelmetaclass): """用戶模型類""" # 類屬性名 表字段 表字段類型 uid = ( 'uid' , 'int unsigned' ) name = ( 'username' , 'varchar(30)' ) email = ( 'email' , 'varchar(30)' ) password = ( 'password' , 'varchar(30)' ) def __init__( self , * * kwargs): for name, value in kwargs.items(): setattr ( self , name, value) def save( self ): fields = [] args = [] for k, v in self .__mappings__.items(): fields.append(v[ 0 ]) args.append( getattr ( self , k, none)) # 表名 table_name = self .__table__ # 數據表中的字段 fields = ',' .join(fields) # 待插入的數據 args = ',' .join([ str (i) for i in args]) # 生成sql語句 sql = f """insert into {table_name} ({fields}) values ({args})""" print (f 'sql: {sql}' ) def main(): user = user(uid = 123 , name = 'hui' , email = 'huidbk@163.com' , password = '123456' ) user.save() if __name__ = = '__main__' : main() |
當 user 指定元類之后,uid、name、email、password 類屬性將不在類中,而是在 __mappings__ 屬性指定的字典中存儲。 user 類的這些屬性將轉變為如下
1
2
3
4
5
6
7
|
__mappings__ = { "uid" : ( 'uid' , "int unsigned" ) "name" : ( 'username' , "varchar(30)" ) "email" : ( 'email' , "varchar(30)" ) "password" : ( 'password' , "varchar(30)" ) } __table__ = "user" |
執行的效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
cls_name - > user bases - > () attrs - > { '__module__' : '__main__' , '__qualname__' : 'user' , '__doc__' : '用戶模型類' , 'uid' : ( 'uid' , 'int unsigned' ), 'name' : ( 'username' , 'varchar(30)' ), 'email' : ( 'email' , 'varchar(30)' ), 'password' : ( 'password' , 'varchar(30)' ), '__init__' : <function user.__init__ at 0x0000026d520c1048 >, 'save' : <function user.save at 0x0000026d520c10d8 > } found mapping: uid = = > ( 'uid' , 'int unsigned' ) found mapping: name = = > ( 'username' , 'varchar(30)' ) found mapping: email = = > ( 'email' , 'varchar(30)' ) found mapping: password = = > ( 'password' , 'varchar(30)' ) sql: insert into user (uid,username,email,password) values ( 123 ,hui,huidbk@ 163.com , 123456 ) |
完善對數據類型的檢測
上面轉成的 sql 語句如下:
1
|
insert into user (uid,username,email,password) values ( 12345 ,hui,huidbk@ 163.com , 123456 ) |
發現沒有,在 sql 語句中字符串類型沒有沒有引號 ''
正確的 sql 語句應該是:
1
|
insert into user (uid,username,email,password) values ( 123 , 'hui' , 'huidbk@163.com' , '123456' ) |
因此修改 user 類完善數據類型的檢測
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
class modelmetaclass( type ): # 此處和上文一樣, 故省略.... pass class user(metaclass = modelmetaclass): """用戶模型類""" uid = ( 'uid' , "int unsigned" ) name = ( 'username' , "varchar(30)" ) email = ( 'email' , "varchar(30)" ) password = ( 'password' , "varchar(30)" ) def __init__( self , * * kwargs): for name, value in kwargs.items(): setattr ( self , name, value) # 在這里完善數據類型檢測 def save( self ): fields = [] args = [] for k, v in self .__mappings__.items(): fields.append(v[ 0 ]) args.append( getattr ( self , k, none)) # 把參數數據類型對應數據表的字段類型 args_temp = list () for temp in args: if isinstance (temp, int ): args_temp.append( str (temp)) elif isinstance (temp, str ): args_temp.append(f "'{temp}'" ) # 表名 table_name = self .__table__ # 數據表中的字段 fields = ',' .join(fields) # 待插入的數據 args = ',' .join(args_temp) # 生成sql語句 sql = f """insert into {table_name} ({fields}) values ({args})""" print (f 'sql: {sql}' ) def main(): user = user(uid = 123 , name = 'hui' , email = 'huidbk@163.com' , password = '123456' ) user.save() if __name__ = = '__main__' : main() |
運行效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
cls_name - > user bases - > () attrs - > { '__module__' : '__main__' , '__qualname__' : 'user' , '__doc__' : '用戶模型類' , 'uid' : ( 'uid' , 'int unsigned' ), 'name' : ( 'username' , 'varchar(30)' ), 'email' : ( 'email' , 'varchar(30)' ), 'password' : ( 'password' , 'varchar(30)' ), '__init__' : <function user.__init__ at 0x0000026d520c1048 >, 'save' : <function user.save at 0x0000026d520c10d8 > } found mapping: uid = = > ( 'uid' , 'int unsigned' ) found mapping: name = = > ( 'username' , 'varchar(30)' ) found mapping: email = = > ( 'email' , 'varchar(30)' ) found mapping: password = = > ( 'password' , 'varchar(30)' ) sql: insert into user (uid,username,email,password) values( 123 , 'hui' , 'huidbk@163.com' , '123456' ) |
抽取到基類中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
# !/usr/bin/python3 # -*- coding: utf-8 -*- # @author: hui # @desc: { 利用python元類實現orm框架的insert插入功能 } # @date: 2021/05/17 17:02 class modelmetaclass( type ): """數據表模型元類""" def __new__(mcs, cls_name, bases, attrs): print (f 'cls_name -> {cls_name}' ) # 類名 print (f 'bases -> {bases}' ) # 繼承類 print (f 'attrs -> {attrs}' ) # 類中所有屬性 print () # 數據表對應關系字典 mappings = dict () # 過濾出對應數據表的字段屬性 for k, v in attrs.items(): # 判斷是否是對應數據表的字段屬性, 因為attrs中包含所有的類屬性 # 這里就簡單判斷字段是元組 if isinstance (v, tuple ): print ( 'found mapping: %s ==> %s' % (k, v)) mappings[k] = v # 刪除這些已經在字典中存儲的字段屬性 for k in mappings.keys(): attrs.pop(k) # 將之前的uid/name/email/password以及對應的對象引用、類名字 # 用其他類屬性名稱保存 attrs[ '__mappings__' ] = mappings # 保存屬性和列的映射關系 attrs[ '__table__' ] = cls_name # 假設表名和類名一致 return type .__new__(mcs, cls_name, bases, attrs) class model( object , metaclass = modelmetaclass): """數據表模型基類""" def __init__( self , * * kwargs): for name, value in kwargs.items(): setattr ( self , name, value) def save( self ): fields = [] args = [] for k, v in self .__mappings__.items(): fields.append(v[ 0 ]) args.append( getattr ( self , k, none)) # 把參數數據類型對應數據表的字段類型 args_temp = list () for temp in args: if isinstance (temp, int ): args_temp.append( str (temp)) elif isinstance (temp, str ): args_temp.append(f "'{temp}'" ) # 表名 table_name = self .__table__ # 數據表中的字段 fields = ',' .join(fields) # 待插入的數據 args = ',' .join(args_temp) # 生成sql語句 sql = f """insert into {table_name} ({fields}) values ({args})""" print (f 'sql: {sql}' ) # 執行sql語句 # ... class user(model): """用戶表模型類""" uid = ( 'uid' , "int unsigned" ) name = ( 'username' , "varchar(30)" ) email = ( 'email' , "varchar(30)" ) password = ( 'password' , "varchar(30)" ) def main(): user = user(uid = 123 , name = 'hui' , email = 'huidbk@163.com' , password = '123456' ) user.save() if __name__ = = '__main__' : main() |
添加數據庫驅動執行sql語句
這里我們使用 pymysql 數據庫驅動,來執行 sql 語句
在 model 類中新增一個 get_connection 的靜態方法用于獲取數據庫連接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
import pymysql class model( object , metaclass = modelmetaclass): """數據表模型基類""" def __init__( self , * * kwargs): for name, value in kwargs.items(): setattr ( self , name, value) @staticmethod def get_connection(): """ 獲取數據庫連接與數據游標 :return: conn, cursor """ conn = pymysql.connect( database = 'testdb' , host = 'localhost' , port = 3306 , user = 'root' , password = '123456' ) return conn, conn.cursor() def save( self ): fields = [] args = [] for k, v in self .__mappings__.items(): fields.append(v[ 0 ]) args.append( getattr ( self , k, none)) # 把參數數據類型對應數據表的字段類型 args_temp = list () for temp in args: if isinstance (temp, int ): args_temp.append( str (temp)) elif isinstance (temp, str ): args_temp.append(f "'{temp}'" ) # 表名 table_name = self .__table__ # 數據表中的字段 fields = ',' .join(fields) # 待插入的數據 args = ',' .join(args_temp) # 生成sql語句 sql = f """insert into {table_name} ({fields}) values ({args})""" print (f 'sql: {sql}' ) # 執行sql語句 conn, cursor = self .get_connection() ret = cursor.execute(sql) print (ret) conn.commit() cursor.close() conn.close() |
添加數據庫驅動執行sql語句
這里我們使用 pymysql 數據庫驅動,來執行 sql 語句
在 model 類中新增一個 get_connection 的靜態方法用于獲取數據庫連接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
import pymysql class model( object , metaclass = modelmetaclass): """數據表模型基類""" def __init__( self , * * kwargs): for name, value in kwargs.items(): setattr ( self , name, value) @staticmethod def get_connection(): """ 獲取數據庫連接與數據游標 :return: conn, cursor """ conn = pymysql.connect( database = 'testdb' , host = 'localhost' , port = 3306 , user = 'root' , password = '123456' ) return conn, conn.cursor() def save( self ): fields = [] args = [] for k, v in self .__mappings__.items(): fields.append(v[ 0 ]) args.append( getattr ( self , k, none)) # 把參數數據類型對應數據表的字段類型 args_temp = list () for temp in args: if isinstance (temp, int ): args_temp.append( str (temp)) elif isinstance (temp, str ): args_temp.append(f "'{temp}'" ) # 表名 table_name = self .__table__ # 數據表中的字段 fields = ',' .join(fields) # 待插入的數據 args = ',' .join(args_temp) # 生成sql語句 sql = f """insert into {table_name} ({fields}) values ({args})""" print (f 'sql: {sql}' ) # 執行sql語句 conn, cursor = self .get_connection() ret = cursor.execute(sql) print (ret) conn.commit() cursor.close() conn.close() |
測試功能
準備數據庫
先準備數據庫 testdb 和 user 數據表
1
2
3
4
5
6
7
8
9
10
|
create database testdb charset = utf8; use testdb; create table user( uid int unsigned auto_increment primary key, username varchar( 30 ) not null, email varchar( 30 ), password varchar( 30 ) not null ); |
user 表結構如下
1
2
3
4
5
6
7
8
|
+ - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - + - - - - - + - - - - - - - - - + - - - - - - - - - - - - - - - - + | field | type | null | key | default | extra | + - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - + - - - - - + - - - - - - - - - + - - - - - - - - - - - - - - - - + | uid | int ( 10 ) unsigned | no | pri | null | auto_increment | | username | varchar( 30 ) | no | | null | | | email | varchar( 30 ) | yes | | null | | | password | varchar( 30 ) | no | | null | | + - - - - - - - - - - + - - - - - - - - - - - - - - - - - - + - - - - - - + - - - - - + - - - - - - - - - + - - - - - - - - - - - - - - - - + |
創建模型類測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class user(model): """用戶表模型類""" uid = ( 'uid' , "int unsigned" ) name = ( 'username' , "varchar(30)" ) email = ( 'email' , "varchar(30)" ) password = ( 'password' , "varchar(30)" ) def main(): user = user(uid = 1 , name = 'hui' , email = 'huidbk@163.com' , password = '123456' ) user.save() for i in range ( 2 , 10 ): user = user( uid = i, name = f 'name{i}' , email = f 'huidbk@16{i}.com' , password = f '12345{i}' ) user.save() if __name__ = = '__main__' : main() |
查看數據庫 user 表數據
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql> select * from user; + - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - - - - + | uid | username | email | password | + - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - - - - + | 1 | hui | huidbk@ 163.com | 123456 | | 2 | name2 | huidbk@ 162.com | 123452 | | 3 | name3 | huidbk@ 163.com | 123453 | | 4 | name4 | huidbk@ 164.com | 123454 | | 5 | name5 | huidbk@ 165.com | 123455 | | 6 | name6 | huidbk@ 166.com | 123456 | | 7 | name7 | huidbk@ 167.com | 123457 | | 8 | name8 | huidbk@ 168.com | 123458 | | 9 | name9 | huidbk@ 169.com | 123459 | + - - - - - + - - - - - - - - - - + - - - - - - - - - - - - - - - - + - - - - - - - - - - + 9 rows in set ( 0.00 sec) |
源代碼
源代碼已上傳到 gitee pythonknowledge: python知識寶庫,歡迎大家來訪。
以上就是用 python 元類的特性實現 orm 框架的詳細內容,更多關于python 實現 orm 框架的資料請關注服務器之家其它相關文章!
原文鏈接:https://juejin.cn/post/6963443372266618917