目錄python
對象關係映射(Object Relational Mapping,簡稱ORM)是經過使用描述對象和數據庫之間映射的元數據,將面嚮對象語言程序中的對象自動持久化到關係數據庫中。本質上就是將數據從一種形式轉換到另一種形式。 這也同時暗示着額外的執行開銷;然而,若是ORM做爲一種中間件實現,則會有不少機會作優化,而這些在手寫的持久層並不存在。 更重要的是用於控制轉換的元數據須要提供和管理;可是一樣,這些花費要比維護手寫的方案要少;並且就算是遵照ODMG規範的對象數據庫依然須要類級別的元數據。數據庫
在對orm進行架構時首先要分清數據庫和對象之間的映射關係:架構
對象與類 | 數據庫 |
---|---|
類名 | 表名 |
對象 | 一條記錄 |
對象.屬性 | 字段 |
這裏將數據庫的增刪改查所有封裝爲一個個的方式,好比:save,delete,update,select等app
具體思路:優化
對字段可能用到的數據類型建立類,而後將這些類實例化出的對象做爲字段類的屬性。code
抽象出父類orm
class Field: def __init__(self,name,column_type,primary_key,default): self.name = name self.column_type = column_type self.primary_key = primary_key self.default = default
子類中間件
class IntegerField(Field): def __init__(self,name,column_type='int',primary_key=False,default=0): super().__init__(name,column_type,primary_key,default) class StringField(Field): def __init__(self,name,column_type='varchar(64)', primary_key=False,default=None): super().__init__(name,column_type,primary_key,default)
抽象出表的父類,這裏表類繼承字典是爲了解決每一張表能夠隨意添加字段的問題,咱們不能每實例化一張表就在表類的__init__中定義新添加的字段名,這樣太麻煩了。經過繼承字典,內部的__init__,能夠接收任意多個關鍵字參數。對象
class Models(dict,metaclass=OrmMetaclass): def __getattr__(self, item): #調用沒有的屬性時觸發 return self.get(item) def __setattr__(self, key, value): #實現將對象.屬性=屬性值轉化爲字典的賦值操做 #給字典對象自己賦值 self[key] = value
子類blog
class User(Models): #屬性名最好與字段類型的名字同名 user_id = IntegerField(name='user_id',primary_key=True) user_name = StringField(name='name') pwd = StringField(name='pwd') #這些屬性都是字段類型類實例化出來的 class Movie(Models): user_id = IntegerField(name='user_id',primary_key=True) user_name = StringField(name='name') pwd = StringField(name='pwd')
繼承字典的類實例化的對象,沒法經過‘對象.屬性’的方式存取值,咱們經過__setattr__,__getattr__來實現,讓字典對象與普通對象如出一轍,而且具有字典對象原有的特性。
元類須要處理的問題:
'''__new__必需要有返回值,返回實例化出來的實例, 這點在本身實現__new__時要特別注意, 能夠return父類__new__出來的實例, 或者直接是object的__new__出來的實例''' class OrmMetaclass(type): def __new__(cls, class_name,class_base,class_dict): #class_base:類名(表名)class_base:基類/父類 class_dict:類的名稱空間 #過濾Models類,若是輸入的是Models類,直接將類返回 if class_name == 'Models': return type.__new__(cls,class_name,class_base,class_dict) table_name = class_dict.get('table_name',class_name) #獲取table_name若是table_name不存在,就獲取class_name primary_key = None 定義一個空的字典,專門用來存放字段對象 mappings = {} 遍歷名稱空間全部的屬性 print(class_dict) ''' {'__module__': '__main__', '__qualname__': 'User', 'user_id': <__main__.IntegerField object at 0x000001C3E13FA7B8>, 'user_name': <__main__.StringField object at 0x000001C3E13FA7F0>, 'pwd': <__main__.StringField object at 0x000001C3E13FA828>} ''' #這裏的每個字段屬性都是對應的字段類實例化出的對象 #key是對象名,value是對象地址 #遍歷類的名稱空間的全部屬性 for key,value in class_dict.items(): #判斷value是不是Field 實例化的對象, # Field是全部字段的父類 if isinstance(value,Field): #將屬性和名稱放入mappings字典裏 mappings[key] = value if value.primary_key: #判斷實例化出來的字段的主鍵屬性是否爲true #若是primary_key有值,則已經有一個字段的primary_key屬性 #設置爲了True if primary_key: raise TypeError('只能有一個主鍵') #若是該字段是主鍵,就將主鍵名賦值給primary_key primary_key = value.name for key in mappings.keys(): class_dict.pop(key) #給類的名稱空間添加表名 class_dict['table_name'] = table_name #給類的名稱空間添加主鍵名 class_dict['primary_key'] = primary_key #給類的名稱空間添加mapping字典,字典中擁有全部的字段屬性 class_dict['mappings'] = mappings return type.__new__(cls,class_name,class_base,class_dict)