激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

腳本之家,腳本語言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - 用 Python 元類的特性實(shí)現(xiàn) ORM 框架

用 Python 元類的特性實(shí)現(xiàn) ORM 框架

2021-11-07 11:27憶想不到的暉 Python

利用 Python 元類的特性實(shí)現(xiàn) ORM 框架的 insert 功能,通過操作類對(duì)象,對(duì)數(shù)據(jù)表進(jìn)行數(shù)據(jù)增加操作。由于 ORM 比較復(fù)雜,也不要重復(fù)造輪子,就完成一個(gè) insert 相類似的ORM,理解其中的道理即可。

orm是什么

o是 object,也就 類對(duì)象 的意思,r是 relation,翻譯成中文是 關(guān)系,也就是關(guān)系數(shù)據(jù)庫中 數(shù)據(jù)表 的意思,m是 mapping,是映射的意思。在orm框架中,它幫我們把類和數(shù)據(jù)表進(jìn)行了一個(gè)映射,可以讓我們通過類和類對(duì)象就能操作它所對(duì)應(yīng)的表格中的數(shù)據(jù)。orm框架還有一個(gè)功能,它可以根據(jù)我們?cè)O(shè)計(jì)的類自動(dòng)幫我們生成數(shù)據(jù)庫中的表,省去了我們自己建表的過程。

一個(gè)句話理解就是:創(chuàng)建一個(gè)實(shí)例對(duì)象,用創(chuàng)建它的類名當(dāng)做數(shù)據(jù)表名,用創(chuàng)建它的類屬性對(duì)應(yīng)數(shù)據(jù)表的字段,當(dāng)對(duì)這個(gè)實(shí)例對(duì)象操作時(shí),能夠?qū)?yīng) mysql 語句。

在 django 中就內(nèi)嵌了一個(gè) orm 框架,不需要直接面向數(shù)據(jù)庫編程,而是定義模型類,通過模型類和對(duì)象完成數(shù)據(jù)表的增刪改查操作。還有第三方庫 sqlalchemy 都是 orm框架。

用 Python 元類的特性實(shí)現(xiàn) ORM 框架

先看看我們大致要實(shí)現(xiàn)什么功能

?
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()
 
# 對(duì)應(yīng)如下sql語句
# insert into user (uid,username,email,password) values (123,hui,huidbk@163.com,123456)

所謂的 orm 就是讓開發(fā)者在操作數(shù)據(jù)庫的時(shí)候,能夠像操作對(duì)象時(shí)通過xxxx.屬性=yyyy一樣簡(jiǎn)單,這是開發(fā)orm的初衷。

實(shí)現(xiàn)orm中的insert功能

通過 python 中 元類 簡(jiǎn)單實(shí)現(xiàn) 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元類簡(jiǎn)單實(shí)現(xiàn)orm框架的insert插入功能 }
# @date: 2021/05/17 17:02
 
 
class modelmetaclass(type):
    """數(shù)據(jù)表模型元類"""
 
    def __new__(mcs, cls_name, bases, attrs):
 
        print(f'cls_name -> {cls_name}')    # 類名
        print(f'bases -> {bases}')          # 繼承類
        print(f'attrs -> {attrs}')          # 類中所有屬性
        print()
 
        # 數(shù)據(jù)表對(duì)應(yīng)關(guān)系字典
        mappings = dict()
 
        # 過濾出對(duì)應(yīng)數(shù)據(jù)表的字段屬性
        for k, v in attrs.items():
            # 判斷是否是指定的stringfield或者integerfield的實(shí)例對(duì)象
            # 這里就簡(jiǎn)單判斷字段是元組
            if isinstance(v, tuple):
                print('found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
 
        # 刪除這些已經(jīng)在字典中存儲(chǔ)的字段屬性
        for k in mappings.keys():
            attrs.pop(k)
 
        # 將之前的uid/name/email/password以及對(duì)應(yīng)的對(duì)象引用、類名字
        # 用其他類屬性名稱保存
        attrs['__mappings__'] = mappings  # 保存屬性和列的映射關(guān)系
        attrs['__table__'] = cls_name     # 假設(shè)表名和類名一致
        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__
        # 數(shù)據(jù)表中的字段
        fields = ','.join(fields)
        # 待插入的數(shù)據(jù)
        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()

當(dāng) user 指定元類之后,uid、name、email、password 類屬性將不在類中,而是在 __mappings__ 屬性指定的字典中存儲(chǔ)。 user 類的這些屬性將轉(zhuǎn)變?yōu)槿缦?/p>

?
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"

執(zhí)行的效果如下:

?
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)

完善對(duì)數(shù)據(jù)類型的檢測(cè)

上面轉(zhuǎn)成的 sql 語句如下:

?
1
insert into user (uid,username,email,password) values (12345,hui,huidbk@163.com,123456)

發(fā)現(xiàn)沒有,在 sql 語句中字符串類型沒有沒有引號(hào) ''

正確的 sql 語句應(yīng)該是:

?
1
insert into user (uid,username,email,password) values (123, 'hui', 'huidbk@163.com', '123456')

因此修改 user 類完善數(shù)據(jù)類型的檢測(cè)

?
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)
 
    # 在這里完善數(shù)據(jù)類型檢測(cè)
    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v[0])
            args.append(getattr(self, k, none))
 
        # 把參數(shù)數(shù)據(jù)類型對(duì)應(yīng)數(shù)據(jù)表的字段類型
        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__
        # 數(shù)據(jù)表中的字段
        fields = ','.join(fields)
        # 待插入的數(shù)據(jù)
        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()

運(yùn)行效果如下:

?
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元類實(shí)現(xiàn)orm框架的insert插入功能 }
# @date: 2021/05/17 17:02
 
 
class modelmetaclass(type):
    """數(shù)據(jù)表模型元類"""
 
    def __new__(mcs, cls_name, bases, attrs):
 
        print(f'cls_name -> {cls_name}'# 類名
        print(f'bases -> {bases}'# 繼承類
        print(f'attrs -> {attrs}'# 類中所有屬性
        print()
 
        # 數(shù)據(jù)表對(duì)應(yīng)關(guān)系字典
        mappings = dict()
 
        # 過濾出對(duì)應(yīng)數(shù)據(jù)表的字段屬性
        for k, v in attrs.items():
            # 判斷是否是對(duì)應(yīng)數(shù)據(jù)表的字段屬性, 因?yàn)閍ttrs中包含所有的類屬性
            # 這里就簡(jiǎn)單判斷字段是元組
            if isinstance(v, tuple):
                print('found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
 
        # 刪除這些已經(jīng)在字典中存儲(chǔ)的字段屬性
        for k in mappings.keys():
            attrs.pop(k)
 
        # 將之前的uid/name/email/password以及對(duì)應(yīng)的對(duì)象引用、類名字
        # 用其他類屬性名稱保存
        attrs['__mappings__'] = mappings  # 保存屬性和列的映射關(guān)系
        attrs['__table__'] = cls_name  # 假設(shè)表名和類名一致
        return type.__new__(mcs, cls_name, bases, attrs)
 
 
class model(object, metaclass=modelmetaclass):
    """數(shù)據(jù)表模型基類"""
 
    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))
 
        # 把參數(shù)數(shù)據(jù)類型對(duì)應(yīng)數(shù)據(jù)表的字段類型
        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__
        # 數(shù)據(jù)表中的字段
        fields = ','.join(fields)
        # 待插入的數(shù)據(jù)
        args = ','.join(args_temp)
 
        # 生成sql語句
        sql = f"""insert into {table_name} ({fields}) values ({args})"""
        print(f'sql: {sql}')
 
        # 執(zhí)行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()

添加數(shù)據(jù)庫驅(qū)動(dòng)執(zhí)行sql語句

這里我們使用 pymysql 數(shù)據(jù)庫驅(qū)動(dòng),來執(zhí)行 sql 語句

在 model 類中新增一個(gè) get_connection 的靜態(tài)方法用于獲取數(shù)據(jù)庫連接

?
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):
    """數(shù)據(jù)表模型基類"""
 
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)
 
    @staticmethod
    def get_connection():
        """
        獲取數(shù)據(jù)庫連接與數(shù)據(jù)游標(biāo)
        :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))
 
        # 把參數(shù)數(shù)據(jù)類型對(duì)應(yīng)數(shù)據(jù)表的字段類型
        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__
        # 數(shù)據(jù)表中的字段
        fields = ','.join(fields)
        # 待插入的數(shù)據(jù)
        args = ','.join(args_temp)
 
        # 生成sql語句
        sql = f"""insert into {table_name} ({fields}) values ({args})"""
        print(f'sql: {sql}')
 
        # 執(zhí)行sql語句
        conn, cursor = self.get_connection()
        ret = cursor.execute(sql)
        print(ret)
        conn.commit()
        cursor.close()
        conn.close()
       

添加數(shù)據(jù)庫驅(qū)動(dòng)執(zhí)行sql語句

這里我們使用 pymysql 數(shù)據(jù)庫驅(qū)動(dòng),來執(zhí)行 sql 語句

在 model 類中新增一個(gè) get_connection 的靜態(tài)方法用于獲取數(shù)據(jù)庫連接

?
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):
    """數(shù)據(jù)表模型基類"""
 
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)
 
    @staticmethod
    def get_connection():
        """
        獲取數(shù)據(jù)庫連接與數(shù)據(jù)游標(biāo)
        :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))
 
        # 把參數(shù)數(shù)據(jù)類型對(duì)應(yīng)數(shù)據(jù)表的字段類型
        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__
        # 數(shù)據(jù)表中的字段
        fields = ','.join(fields)
        # 待插入的數(shù)據(jù)
        args = ','.join(args_temp)
 
        # 生成sql語句
        sql = f"""insert into {table_name} ({fields}) values ({args})"""
        print(f'sql: {sql}')
 
        # 執(zhí)行sql語句
        conn, cursor = self.get_connection()
        ret = cursor.execute(sql)
        print(ret)
        conn.commit()
        cursor.close()
        conn.close()
       

測(cè)試功能

準(zhǔn)備數(shù)據(jù)庫

先準(zhǔn)備數(shù)據(jù)庫 testdb 和 user 數(shù)據(jù)表

?
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 表結(jié)構(gòu)如下

?
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    |                |
+----------+------------------+------+-----+---------+----------------+

創(chuàng)建模型類測(cè)試

?
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()
   

查看數(shù)據(jù)庫 user 表數(shù)據(jù)

?
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知識(shí)寶庫,歡迎大家來訪。

以上就是用 python 元類的特性實(shí)現(xiàn) orm 框架的詳細(xì)內(nèi)容,更多關(guān)于python 實(shí)現(xiàn) orm 框架的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!

原文鏈接:https://juejin.cn/post/6963443372266618917

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品亚洲二区 | 九九热久久免费视频 | 1级黄色毛片 | 在线成人免费观看 | av电影网在线观看 | 国产精品视频一区二区三区四 | 在线天堂中文字幕 | 黄色大片在线观看 | 国内精品久久久久久久久久 | 一级性生活免费视频 | 毛片在线免费播放 | 成人性生活视频 | 国产乱free国语对白 | 日本一区二区在线 | 性片久久 | 天堂在线资源av | 99爱精品在线| 人成免费网站 | 日韩欧美动作影片 | 久久久国产精品网站 | 手机国产乱子伦精品视频 | 精品国产一区二区三区在线观看 | 综合图区亚洲 | 国产精品久久久久久久四虎电影 | 精品一区二区久久久久久按摩 | 91短视频在线观看视频 | 成人免费网站在线观看视频 | 狠狠干五月天 | 88xx成人永久免费观看 | 欧美有码在线观看 | 日本久久精品视频 | 成人一级片毛片 | 伊人亚洲精品 | lutube成人福利在线观看污 | 色柚视频网站ww色 | 性大片性大片免费 | 国产午夜精品一区二区三区在线观看 | 福利一区二区三区视频在线观看 | 极品大长腿啪啪高潮露脸 | 欧美在线电影 | 亚洲精品久久久久www |