ORM模型:
ORM模型對于后端開發來說肯定是不陌生的,包括很多后端框架比如django,現在都自帶這個模型了
ORM(Object Relational Mapping)對象關系映射
Python中的類與數據庫之間的映射,對數據的操作就不用編寫SQL語言了,因為都封裝好了,比如你想插入一條數據,你就直接創建一個對象即可,
類名 ------->>>> 數據庫中的表名
屬性 ------->>>> 數據庫中的字段
對象 ------->>>> 數據庫中的一行數據
大致就是上面的映射關系
ORM實現步驟:
1、利用描述器實現對數據庫字段的類型、長度限制
2、實現模型類,也就是創建一張表并定義字段
3、利用元類實現映射關系
元類:
1.實例對象是通過類來創建,那類又是通過什么創建的呢?python中所有的類都是通過元類來創建類,且這個元類只有一個那就是type;
1
2
3
4
5
6
7
8
9
|
class Test: pass t = Test() print ( type (Test)) #類是通過元類type創建 print ( type (t)) #實例對象是通過類創建 #輸出----------------------- < class 'type' > < class '__main__.Test' > |
2.除了通過class關鍵字創建類,我們也可以通過元類type來創建類,type作為python的內置函數,其實他也是一個類,他有兩個作用:
1.傳入一個變量,返回這個變量的類型;
2.創建類時,第一個參數傳入類名,第二個參數傳入一個元祖,指定父類,第三個參數傳入字典設置類的屬性或方法;
1
2
3
4
5
6
7
8
9
|
#通過元類來創建類 test1 = type ( "test2" ,( object ,),{ "name" : "xiaoming" }) t1 = test1() print ( type (test1)) print ( type (t1)) #輸出--------------- < class 'type' > < class '__main__.test2' > |
自定義元類:
1.自定義一個元類,這個元類必須繼承type,用這個元類創建類時,必須使用metaclass指定他的元類;
1
2
3
4
5
6
7
8
9
10
11
|
class MetaClass( type ): """自定義一個元類,這個元類必須繼承type,用這個元類創建類時,必須使用metaclass指定他的元類""" def __new__( cls , name, bases, dic, * args, * * kwargs): return super ().__new__( cls , name, bases, dic) class Test(metaclass = MetaClass): name = "xiaoming" print ( type (Test)) #輸出---------------- < class '__main__.MetaClass' > #可以看到此時這個類創建是用我們自定義的元類創建的,而不是type創建的 |
描述器:
1.描述器的定義,只要一個類中實現了__get__、set、__delete__中的一個或幾個,這個類的實例就可以叫描述器,那描述器有神么作用呢,在我們給描述器設置屬性時,一定會調用描述器里面的—set—方法,那我們在設置屬性之前是不是可以在set方法里面,做一些手腳,比如校驗這個屬性長度,類型等等,如果不符合要求那就拋錯,如果符合那就設置成功;
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
|
class Describer: """ 這是一個描述器,描述器一般不直接實例化對象,而是在另一個類里,給他設置屬性 """ def __set__( self , instance, value): print ( "設置屬性的時候會被調用" ) self .value = value def __get__( self , instance, owner): print ( "獲取屬性的時候會被調用" ) return self .value def __delete__( self , instance): print ( "刪除屬性的時候會被調用" ) self .value = None class Test: name = Describer() t = Test() t.name = "xiaoming" #給描述器設置一個類屬性 print (t.name) #獲取一個類屬性 #輸出--------------- 設置屬性的時候會被調用 獲取屬性的時候會被調用 xiaoming |
利用描述器實現對數據庫字段的類型、長度限制
設置屬性一定會調用描述器里面的—set—方法,再設置屬性之前校驗這個屬性長度,類型等等,如果不符合要求那就拋錯,如果符合那就設置成功;
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
|
class BaseFiled: #為什么要先定義一個父類呢,一會我們改造元類的時候會用到 pass class CharFiled(BaseFiled): """定義一個字符串的類型限制""" def __init__( self , length = 10 ): self .length = length def __set__( self , instance, value): if isinstance (value, str ): if len (value) < = self .length: self .value = value else : raise ValueError( "length can not exceed {}" . format ( self .length)) else : raise TypeError( "need a str" ) def __get__( self , instance, owner): return self .value def __delete__( self , instance): self .value = None class IntFiled(BaseFiled): """定義一個數值的類型限制""" def __set__( self , instance, value): if isinstance (value, int ): self .value = value else : raise TypeError( "need a int" ) def __get__( self , instance, owner): return self .value def __delete__( self , instance): self .value = None |
實現模型類,也就是創建一張表并定義字段:
模型類,這些字段是通過描述器來進行長度和類型校驗的
1
2
3
4
5
|
class User(): """這是一個模型類,相當于定義了一個表名是User的表,表有三個字段name,age,love""" name = CharFiled() age = IntFiled() love = CharFiled(length = 50 ) |
如果是這樣的話我們給這個類實例化的時候,需要這樣傳值:
1
2
3
4
|
t1 = User() t1.name = "xiaoming" t1.age = 18 t1.love = "single" |
如果我們需要一次性傳入三個值得話,那就需要一個__init__來接收,但是模型類一般里面不定義init,所以我們可以給他找個父類,然后讓他繼承父類的init,這樣我們就可以一次傳入多個值,并且參數個數也不限定,優化如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class BaseMode(): def __init__( self , * * kwargs): """ 由于每一個模型類(也就是數據庫表)的屬性個數不一致,所以我們需要定義一個父類來進行定義初始化的屬性 :param kwargs: """ for k, v in kwargs.items(): # 遍歷傳進來的所有屬性 setattr ( self , k, v) # 拿到這些屬性后對self(也就是類本身)進行設置屬性 class User(BaseMode): """第一個模型類""" name = CharFiled() age = IntFiled() love = CharFiled(length = 50 ) t1 = User(name = "xiaoming" ,age = 18 ,love = "xxx" ) #現在我們就可以一次傳入多個值,并且參數個數也不限定 class Oder(BaseMode): """第二個模型類""" id = CharFiled() count = IntFiled() t2 = Oder( id = "a1" ,count = 123 ) |
利用元類實現模型類和數據庫映射關系:
其實就是創建模型類的時候,提取模型類的類名作為表名,提取模型類的屬性作為字段名,存儲在模型類的類屬性里,然后備用,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class MetaClass( type ): """自定義一個元類,這個元類必須繼承type,用這個元類創建類時,必須使用meteclass指定他的元類""" def __new__( cls , name, bases, dic, * args, * * kwargs): if name = = "BaseMode" : # (User,Oder,BaserMode都指定了用這個元類)我們的目的是對模型類(User,Oder)進行提取操作,不對BaserMode進行操作,所以先判斷類名是否為BaseMode,如果是則直接使用元類創建類,不需要提取 return super ().__new__( cls , name, bases, dic) else : table_name = name.lower() # 提取表名,即模型類的類名,將表名變成小寫 filed_dic = {} # 定義一個空的列表,用來裝dic中(dic中的屬性就是模型類里面我們寫的字段)屬于BaseFiled類型的屬性,因為dic中會有其他創建類時自動生成的屬性(例如__開頭的一些),這些屬性我們沒必要去建立映射關系,所以需要將其剔除掉 for k, v in dic.items(): if isinstance (v,BaseFiled): filed_dic[k] = v dic[ "t_name" ] = table_name # 給dic新加一個t_name的屬性,將表名添加到dic中,實現類名與表名的映射關系 dic[ "filed_dict" ] = filed_dic # 給dic新加一個filed_dict的屬性,將屬于BaseFiled類型的屬性給添加到dic中,實現屬性與字段的映射關系 return super ().__new__( cls , name, bases,dic) |
**創建模型類的時候,利用給他指定元類,提取了表名和字段名,我們具體要怎么用呢?**我們可以在模型類的父類里面定義一個save方法,然后用這些字段寫sql,在我們創建模型類的實例后,就調用這個save方法,即可向數據庫插入一條數據了,如下:
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
|
class BaseMode(metaclass = MetaClass): def __init__( self , * * kwargs): """ 由于每一個模型類(也就是數據庫表)的屬性個數不一致,所以我們需要定義一個父類來進行定義初始化的屬性 :param kwargs: """ for k, v in kwargs.items(): # 遍歷傳進來的所有屬性 setattr ( self , k, v) # 拿到這些屬性后對self(也就是類本身)進行設置屬性 def save( self ): """生成SQL語句""" # 獲取表名 table_name = self .t_name # 獲取所有的屬性 fileds = self .filed_dict dic = {} # 定義一個空字典,用來裝屬性名和屬性值 for k, v in fileds.items(): value = getattr ( self , k) dic[k] = value sql = "insert into {} values{}" . format (table_name, tuple (dic.values())) print (sql) return sql class User(BaseMode): name = CharFiled() age = IntFiled() love = CharFiled(length = 50 ) class Oder(BaseMode): """第二個模型類""" id = CharFiled() count = IntFiled() t2 = Oder( id = "a1" ,count = 123 ) #實例化模型類,并調用save方法,即可向數據庫插入數據 t2.save() t1 = User(name = "xiaoming" ,age = 18 ,love = "xxx" ) t1.save() #輸出----------------------- insert into oder values( 'a1' , 123 ) insert into user values( 'xiaoming' , 18 , 'xxx' ) |
至此我們的一個簡單的ORM模型就定義好了,我們只需要理解原理即可,因為一個成熟的后端框架都有現成的ORM模型,例如django和flask都自帶的有,最后再附上全部代碼,如果有幫到你,能點個贊嗎,謝謝咯~~~
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
93
94
95
96
97
98
|
class MetaClass( type ): """自定義一個元類,這個元類必須繼承type,用這個元類創建類時,必須使用meteclass指定他的元類""" def __new__( cls , name, bases, dic, * args, * * kwargs): if name = = "BaseMode" : # (User,Oder,BaserMode都指定了用這個元類)我們的目的是對模型類(User,Oder)進行提取操作,不對BaserMode進行操作,所以先判斷類名是否為BaseMode,如果是則直接使用元類創建類,不需要提取 return super ().__new__( cls , name, bases, dic) else : table_name = name.lower() # 提取表名,即模型類的類名,將表名變成小寫 filed_dic = {} # 定義一個空的列表,用來裝dic中(dic中的屬性就是模型類里面我們寫的字段)屬于BaseFiled類型的屬性,因為dic中會有其他創建類時自動生成的屬性(例如__開頭的一些),這些屬性我們沒必要去建立映射關系,所以需要將其剔除掉 for k, v in dic.items(): if isinstance (v,BaseFiled): filed_dic[k] = v dic[ "t_name" ] = table_name # 給dic新加一個t_name的屬性,將表名添加到dic中,實現類名與表名的映射關系 dic[ "filed_dict" ] = filed_dic # 給dic新加一個filed_dict的屬性,將屬于BaseFiled類型的屬性給添加到dic中,實現屬性與字段的映射關系 return super ().__new__( cls , name, bases,dic) class BaseFiled: pass class CharFiled(BaseFiled): """定義一個字符串的類型限制""" def __init__( self , length = 10 ): self .length = length def __set__( self , instance, value): if isinstance (value, str ): if len (value) < = self .length: self .value = value else : raise ValueError( "length can not exceed {}" . format ( self .length)) else : raise TypeError( "need a str" ) def __get__( self , instance, owner): return self .value def __delete__( self , instance): self .value = None class IntFiled(BaseFiled): """定義一個數值的類型限制""" def __set__( self , instance, value): if isinstance (value, int ): self .value = value else : raise TypeError( "need a int" ) def __get__( self , instance, owner): return self .value def __delete__( self , instance): self .value = None class BaseMode(metaclass = MetaClass): def __init__( self , * * kwargs): """ 由于每一個模型類(也就是數據庫表)的屬性個數不一致,所以我們需要定義一個父類來進行定義初始化的屬性 :param kwargs: """ for k, v in kwargs.items(): # 遍歷傳進來的所有屬性 setattr ( self , k, v) # 拿到這些屬性后對self(也就是類本身)進行設置屬性 def save( self ): """生成SQL語句""" # 獲取表名 table_name = self .t_name # 獲取所有的屬性 fileds = self .filed_dict dic = {} # 定義一個空字典,用來裝屬性名和屬性值 for k, v in fileds.items(): value = getattr ( self , k) dic[k] = value sql = "insert into {} values{}" . format (table_name, tuple (dic.values())) print (sql) return sql class User(BaseMode): name = CharFiled() age = IntFiled() love = CharFiled(length = 50 ) class Oder(BaseMode): """第二個模型類""" id = CharFiled() count = IntFiled() t2 = Oder( id = "a1" ,count = 123 ) t2.save() t1 = User(name = "xiaoming" ,age = 18 ,love = "xxx" ) t1.save() |
到此這篇關于python利用元類和描述器實現ORM模型的文章就介紹到這了,更多相關python ORM模型內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/weixin_48636525/article/details/121474590