orm

orm

對象關係映射(Object Relational Mapping,簡稱ORM)是經過使用描述對象和數據庫之間映射的元數據,將面嚮對象語言程序中的對象自動持久化到關係數據庫中。本質上就是將數據從一種形式轉換到另一種形式。 這也同時暗示着額外的執行開銷;然而,若是ORM做爲一種中間件實現,則會有不少機會作優化,而這些在手寫的持久層並不存在。 更重要的是用於控制轉換的元數據須要提供和管理;可是一樣,這些花費要比維護手寫的方案要少;並且就算是遵照ODMG規範的對象數據庫依然須要類級別的元數據。數據庫

在對orm進行架構時首先要分清數據庫和對象之間的映射關係:架構

對象與類 數據庫
類名 表名
對象 一條記錄
對象.屬性 字段

這裏將數據庫的增刪改查所有封裝爲一個個的方式,好比:save,delete,update,select等app

具體思路:優化

1、數據類型類的定義

對字段可能用到的數據類型建立類,而後將這些類實例化出的對象做爲字段類的屬性。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)

2、定義表類

抽象出表的父類,這裏表類繼承字典是爲了解決每一張表能夠隨意添加字段的問題,咱們不能每實例化一張表就在表類的__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')

3、引入元類

繼承字典的類實例化的對象,沒法經過‘對象.屬性’的方式存取值,咱們經過__setattr__,__getattr__來實現,讓字典對象與普通對象如出一轍,而且具有字典對象原有的特性。

元類須要處理的問題:

  1. 強制數據表類有且只有一個主鍵。
  2. 將數據表中全部的字段對象都存放在一個獨立的字典中,方便取用。
'''__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)
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息