ORM對象關係映射框架基本搭建

一 概念

1 概念

ORM :對象關係映射,對象和關係之間的映射,使用面向對象的方式來操做數據庫 python


關係對象模型和python對象模型之間的映射 mysql


tabel => class ,表映射類
row => object ,行映射爲實例
column=> property ,字段映射屬性 sql

2 舉例

表有login,字段爲id int , username varchar, age int
映射到python爲 數據庫

#!/usr/bin/poython3.6
#conding:utf-8

class  Login:
    # 此處的INTX 是int類型的類 
    #  VARY 是varchar類型的類
    id=INTX()
    username=VARY()
    age=INTX()
# 最終獲得 
class Login:
    def __init__(self):
        self.id=1
        self.username='admin'
        self.age=20

二 實現ORM 框架

1 字段類的實現

字段有name,字段名稱爲column,類型爲type,是否主鍵pk,是否惟一鍵unique,是否索引index,是否可爲空nullable,默認值default,是否自增等,這些都是字段的特徵,因此字段可使用類來描述。markdown

字段類要提供對數據的校驗功能,如聲明字段類型爲int類型,應該要判斷數據是否是整數類形。
字段有多種類型,不一樣類型有差別,使用繼承的方式實現。
字段如今定義爲類屬性,而這個類屬性又適合使用類來描述,這就是描述器了。session


1 定義基類,用於實現全部類的基礎類型 app

#!/usr/bin/poython3.6
#conding:utf-8

class  Field:
    def __init__(self,name,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        self.name=name  # 字段名稱
        if  column  is None:  #列名稱
            self.column=self.name
        else:
            self.column=column
        self.pk=pk  # 主鍵
        self.unique=unique  #惟一
        self.index=index #索引
        self.nullable=nullable  #是否爲空
        self.default=default  # 默認是否爲空
    def  validate(self,value):  # 此處定義數據校驗方式,每種不一樣類型的校驗方式不一樣,所以應該在子類中分別實現
        raise  NotImplementedError  #基類不實現此功能

    def __get__(self, instance, owner): #此處用於定義描述器,此處當子類的類調用此屬性時,會返回對應的值
        # 此處的self表示父類的實例,instance表示子類的實例,owner表示子類的類
        # pass
        if instance is None:  #此處爲None表示子類未生成對應實例
            return self  # 此處的self表示實例本身
        return  instance.__dict__[self.name]  # 返回實例對應的字段名稱
    def __set__(self, instance, value): #此處用於定義數據描述器,用於子類實例調用時使用,用於返回對應的結果
        # instace 表示子類的實例,其相關的信息應該被存儲於子類實例中,
        self.validate(value)
        instance.__dict__[self.name]=value

    def  __str__(self):
        return   "{} <{}>".format(self.__class__.__name__,self.name)  # 此處返回被調用的類名和實例名稱
    __repr__=__str__
# 定義整數類型的類型屬性
class  IntField(Field):  #多了自增屬性。
    def  __init__(self,name,column=None,pk=False,unique=False,index=False,nullable=True,default=None,auto_increment=True):
        self.auto_increment=auto_increment
        super().__init__(name,column,pk,unique,index,nullable,default)

    def validate(self,value):
        if value is  None:
            if self.pk:  # 主鍵不能爲空,所以此處會報錯
                raise TypeError("{}:{}".format(self.name,value))
            if not self.nullable:  # 當定義了非空時,上述的值爲空,則報錯
                raise TypeError
        else:
            if not isinstance(value,int):  #若數據的類型爲非int,則報錯
                raise TypeError("{}  is  not  int,  It's {}".format(self.name,type(value)))

# 定義字符串的類型屬性
class  StringField(Field):  #定義字符串屬性類
    # 增長了字符串長度的定義
    def __init__(self,length=32,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        super().__init__(name,column,pk,unique,index,nullable,default)  #此處的屬性能夠繼承父類的屬性
        self.length=length  #此處用於定義字符串類型的長度
    def  validate(self,value):  #此處用於定義各自的屬性檢查,對數據進行屬性檢查
        if  value  is None: # 此處的None對應數據庫的null
            if self.pk:  # 若是數據是None,而其定義了主鍵,則會報錯,由於主鍵必須不能是Null,主鍵非空且惟一
                raise TypeError("{} is pk,not None".format(self.name))
            if not  self.nullable:  # 若是其是None,而定義的是非null,則會報錯
                raise TypeError("{}  is  not  null".format(self.name))
        else:
            if not isinstance(value,str):
                raise TypeError("{} should be string".format(self.name))
            if len(value) > self.length:  #真實的值大於規定的值,則會報錯
                raise ValueError("{} is to long value={}".format(self.name,value))

2 Login 類的實現

# 具體類的實現

class  Login:
    id=IntField('id','id',pk=True,nullable=False,auto_increment=True)  # 此種調用方式會啓動get方法的調用,從而返回實例本身
    name=StringField(length=64,name='username',nullable=False)
    age=IntField('age')
    def  __init__(self,id,nane,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__

Login 類的操做
Login類的操做對應表的CRUD操做,及增刪改查,若是使用pymysql,應該使用cursor對象的execute方法,增長數據,修改數據定義爲save方法,爲Login類增長此方法,數據庫的連接要求從外面傳入 框架

具體實現以下 ide

# 具體類的實現

# 具體類的實現

class  Login:
    id=IntField('id','id',pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,name='username',nullable=False)
    age=IntField('age')
    def  __init__(self,id,nane,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__

    def save(self,conn:pymysql.connections.Connection):
        sql="insert into  login(id,bane,age)  values(%s,%s,%s)"
        with conn as cursor:
            cursor.execute(sql,(self.id,self.name,self.age))

3 session類的實現

每一次數據庫操做都是在一個會話中完成的,將cursor的操做封裝在會話中線程

class  Session:  #此處用以封裝連接,可在此處增長上下文支持
    def __init__(self,conn:pymysql.connections.Connection):
        self.conn=conn
        self.cursor=None
    def execute(self,query,*args):
        if self.cursor is None:
            self.cursor=self.conn.cursor()
        self.cursor.execute(query,args)
    def __enter__(self): # 此處實現方式和
        return self.conn.cursor()

        # self.cursor=self.conn.cursor()
        # return self 如此寫,這個session必須是一個線程級別的,若是用進程,則直接覆蓋cursor
        # #由於線程是順序執行的,都用新的cursor()當查詢數據時,數據找不到了,由於cursor變了。本session是在線程內執行,不能誇線程執行

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        if exc_type: # 此處用於定義是否出錯,若出錯,則直接返回
            self.conn.rollback()
        else:
            self.conn.commit()

class  Login:
    id=IntField('id','id',pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,name='username',nullable=False)
    age=IntField('age')
    def  __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__

    def save(self,session:Session):
        sql="insert  into  login(id,name,age)  values(%s,%s,%s)"
        with  session as cursor:  # 此處直接調用enter返回cursor遊標,此處的最後會關閉遊標,但不會關閉連接
            with cursor:  # 此處使用遊標的屬性進行處理 
                cursor.execute(sql,(self.id,self.name,self.age))

4 Model 類的實現

Login 這樣的類,若是多創建幾個,其都是一個樣子,每個這樣的類,得定義一個名稱對應不一樣的表,都須要先定義好類,再定義__init__初始化值,而這些值恰好是定義好的類屬性,操做也是同樣的。CRID

設計,定義一個Model類,增長一個__table__類屬性來保存不一樣的表名稱

class  Model:
    def save(self,session:Session=None):
        names=[]
        values=[]
        for k,v  in self.__class__.__dict__.items():  # 此處用於獲取實例的名稱和其對應的值
            # print ('for',k,'---',v)
            if  isinstance(v,Field):  # 此處若屬於基類
                if  k in self.__dict__.keys():  # 此處的字段符合
                    names.append(k)
                    values.append(v)
        # __table__  # 此處在子類中添加
        query="insert into {} ({}) values ({})".format(self.__table__,",".join(names),",".join(["%s"]*len(values))) # 此處是匹配對應的sql
        print (query)
        print (values)

class  Login(Model):
    id=IntField('id','id',pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,name='name',nullable=False)
    age=IntField('age')
    __table__='Login'
    def  __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__

5 使用元類改造Model

編寫一個元類ModelMeta
以它做爲元類的類,均可以得到一個類屬性
若是沒有定義_table_,就自動加上這個屬性,值爲類名
能夠遍歷類屬性,找出定義的字段類,創建一張映射表mapping
找出主鍵字段primarykey

#!/usr/bin/poython3.6
#conding:utf-8
import  pymysql
class  Field:
    def __init__(self,name,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        self.name=name  # 字段名稱
        if  column  is None:  #列名稱
            self.column=self.name
        else:
            self.column=column
        self.pk=pk  # 主鍵
        self.unique=unique  #惟一
        self.index=index #索引
        self.nullable=nullable  #是否爲空
        self.default=default  # 默認是否爲空
    def  validate(self,value):  # 此處定義數據校驗方式,每種不一樣類型的校驗方式不一樣,所以應該在子類中分別實現
        raise  NotImplementedError  #基類不實現此功能

    def __get__(self, instance, owner): #此處用於定義描述器,此處當子類的類調用此屬性時,會返回對應的值
        # 此處的self表示父類的實例,instance表示子類的實例,owner表示子類的類
        # pass
        if instance is None:  #此處爲None表示子類未生成對應實例
            return self  # 此處的self表示實例本身
        return  instance.__dict__[self.name]  # 返回實例對應的字段名稱
    def __set__(self, instance, value): #此處用於定義數據描述器,用於子類實例調用時使用,用於返回對應的結果
        # instace 表示子類的實例,其相關的信息應該被存儲於子類實例中,
        self.validate(value)
        instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對應的屬性的值,及真實的數據

    def  __str__(self):
        return   "{} <{}>".format(self.__class__.__name__,self.name)  # 此處返回被調用的類名和實例名稱
    __repr__=__str__
# 定義整數類型的類型屬性
class  IntField(Field):  #多了自增屬性。
    def  __init__(self,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None,auto_increment=True):
        self.auto_increment=auto_increment
        super().__init__(name,column,pk,unique,index,nullable,default)

    def validate(self,value):
        if value is  None:
            if self.pk:  # 主鍵不能爲空,所以此處會報錯
                raise TypeError("{}:{}".format(self.name,value))
            if not self.nullable:  # 當定義了非空時,上述的值爲空,則報錯
                raise TypeError
        else:
            if not isinstance(value,int):  #若數據的類型爲非int,則報錯
                raise TypeError("{}  is  not  int,  It's {}".format(self.name,type(value)))

# 定義字符串的類型屬性
class  StringField(Field):  #定義字符串屬性類
    # 增長了字符串長度的定義
    def __init__(self,length=32,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        super().__init__(name,column,pk,unique,index,nullable,default)  #此處的屬性能夠繼承父類的屬性
        self.length=length  #此處用於定義字符串類型的長度
    def  validate(self,value):  #此處用於定義各自的屬性檢查,對數據進行屬性檢查
        if  value  is None: # 此處的None對應數據庫的null
            if self.pk:  # 若是數據是None,而其定義了主鍵,則會報錯,由於主鍵必須不能是Null,主鍵非空且惟一
                raise TypeError("{} is pk,not None".format(self.name))
            if not  self.nullable:  # 若是其是None,而定義的是非null,則會報錯
                raise TypeError("{}  is  not  null".format(self.name))
        else:
            if not isinstance(value,str):
                raise TypeError("{} should be string".format(self.name))
            if len(value) > self.length:  #真實的值大於規定的值,則會報錯
                raise ValueError("{} is to long value={}".format(self.name,value))

# 具體類的實現
class  Session:  #此處用以封裝連接,可在此處增長上下文支持
    def __init__(self,conn:pymysql.connections.Connection):
        self.conn=conn
        self.cursor=None
    def execute(self,query,*args):
        if self.cursor is None:
            self.cursor=self.conn.cursor()
        self.cursor.execute(query,args)
    def __enter__(self): # 此處實現方式和
        return self.conn.cursor()

        # self.cursor=self.conn.cursor()
        # return self 如此寫,這個session必須是一個線程級別的,若是用進程,則直接覆蓋cursor
        # #由於線程是順序執行的,都用新的cursor()當查詢數據時,數據找不到了,由於cursor變了。本session是在線程內執行,不能誇線程執行

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        if exc_type: # 此處用於定義是否出錯,若出錯,則直接返回
            self.conn.rollback()
        else:
            self.conn.commit()

class ModeMetd(type):  # 子類構建的時候實現的
    def __new__(cls,name,bases,attrs:dict):  # 此處是在類的定義中進行調用的,
        # 此處的name表示被調用子類的類名,而對應的字典attrs則表示子類的屬性字典
        # name 類名,attrs類屬性字典
        if '__table__'  not in attrs.keys():
            attrs['__table__']=name   #默認添加表名稱爲類名稱

        mapping={}  # 方便後面查詢屬性名和字段實例
        primarykey=[]  # 多個主鍵的狀況下使用,若是使用一個變量名,則致使後面覆蓋前面
        for k,v in attrs.items():  # k表明的是列名稱,v表示的是子類的類型
            if isinstance(v,Field):  # 此處判斷是否繼承與父類
                mapping[k]=v
                if  v.name is None:  # 此處用於處理子類的屬性的字典的列名稱處理問題
                    v.name=k  # 若是是,則將類.name
                if  v.column is None:
                    v.column=v.name # 沒有給字段名,則使用類對應的列名稱
                if v.pk:
                    primarykey.append(v)
        # 增長屬性
        attrs['__mapping__']=mapping
        attrs['__primarykey__']=primarykey
        return super().__new__(cls,name,bases,attrs)

class  Model(metaclass=ModeMetd):  # 實體類的調用時實現,子類的實例調用時實現的
    def save(self,session:Session=None):
        names=[]
        values=[]
        for k,v  in self.__class__.__dict__.items():  # 此處用於獲取實例的名稱和其對應的值
            # print ('for',k,'---',v)
            if  isinstance(v,Field):  # 此處若屬於基類
                if  k in self.__dict__.keys():  # 此處的字段符合
                    names.append(k)
                    values.append(self.__dict__[k]) # v是一個Field類型的實例,是子類和父類的實例
                    print (self.__dict__[k])  # 此處是實例。實例中的數字
        # __table__  # 此處在子類中添加
        query="insert into {} ({}) values ({})".format(self.__table__,",".join(names),",".join(["%s"]*len(values))) # 此處是匹配對應的sql
        print (query)
        # with  session:
        #     session.execute(query,values)

class  Login(Model):
    id=IntField(pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,name='name',nullable=False)
    age=IntField()
    __table__='Login'
    def  __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__

l=Login(1,'admin',20)
l.save(None)
if __name__ == "__main__":
    pass

結果以下

ORM對象關係映射框架基本搭建

6 引擎類

實體類沒有提供數據庫鏈接,固然也不該該提供,實體類就應該只完成表和類的映射。

提供一個數據庫的包裝類
1 負責數據庫鏈接
2 負責CRUD操做,取代實體類的CRUD方法

#!/usr/bin/poython3.6
#conding:utf-8
import  pymysql
class  Field:
    def __init__(self,name,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        self.name=name  # 字段名稱
        if  column  is None:  #列名稱
            self.column=self.name
        else:
            self.column=column
        self.pk=pk  # 主鍵
        self.unique=unique  #惟一
        self.index=index #索引
        self.nullable=nullable  #是否爲空
        self.default=default  # 默認是否爲空
    def  validate(self,value):  # 此處定義數據校驗方式,每種不一樣類型的校驗方式不一樣,所以應該在子類中分別實現
        raise  NotImplementedError  #基類不實現此功能

    def __get__(self, instance, owner): #此處用於定義描述器,此處當子類的類調用此屬性時,會返回對應的值
        # 此處的self表示父類的實例,instance表示子類的實例,owner表示子類的類
        # pass
        if instance is None:  #此處爲None表示子類未生成對應實例
            return self  # 此處的self表示實例本身
        return  instance.__dict__[self.name]  # 返回實例對應的字段名稱
    def __set__(self, instance, value): #此處用於定義數據描述器,用於子類實例調用時使用,用於返回對應的結果
        # instace 表示子類的實例,其相關的信息應該被存儲於子類實例中,
        self.validate(value)
        instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對應的屬性的值,及真實的數據

    def  __str__(self):
        return   "{} <{}>".format(self.__class__.__name__,self.name)  # 此處返回被調用的類名和實例名稱
    __repr__=__str__
# # 定義整數類型的類型屬性
class  IntField(Field):  #多了自增屬性。
    def  __init__(self,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None,auto_increment=True):
        self.auto_increment=auto_increment
        super().__init__(name,column,pk,unique,index,nullable,default)

    def validate(self,value):
        if value is  None:
            if self.pk:  # 主鍵不能爲空,所以此處會報錯
                raise TypeError("{}:{}".format(self.name,value))
            if not self.nullable:  # 當定義了非空時,上述的值爲空,則報錯
                raise TypeError
        else:
            if not isinstance(value,int):  #若數據的類型爲非int,則報錯
                raise TypeError("{}  is  not  int,  It's {}".format(self.name,type(value)))

# 定義字符串的類型屬性
class  StringField(Field):  #定義字符串屬性類
    # 增長了字符串長度的定義
    def __init__(self,length=32,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        super().__init__(name,column,pk,unique,index,nullable,default)  #此處的屬性能夠繼承父類的屬性
        self.length=length  #此處用於定義字符串類型的長度
    def  validate(self,value):  #此處用於定義各自的屬性檢查,對數據進行屬性檢查
        if  value  is None: # 此處的None對應數據庫的null
            if self.pk:  # 若是數據是None,而其定義了主鍵,則會報錯,由於主鍵必須不能是Null,主鍵非空且惟一
                raise TypeError("{} is pk,not None".format(self.name))
            if not  self.nullable:  # 若是其是None,而定義的是非null,則會報錯
                raise TypeError("{}  is  not  null".format(self.name))
        else:
            if not isinstance(value,str):
                raise TypeError("{} should be string".format(self.name))
            if len(value) > self.length:  #真實的值大於規定的值,則會報錯
                raise ValueError("{} is to long value={}".format(self.name,value))

# 具體類的實現
class  Session:  #此處用以封裝連接,可在此處增長上下文支持
    def __init__(self,conn:pymysql.connections.Connection):
        self.conn=conn
        self.cursor=None
    def execute(self,query,*args):
        if self.cursor is None:
            self.cursor=self.conn.cursor()
        self.cursor.execute(query,args)
    def __enter__(self): # 此處實現方式和
        return self.conn.cursor()

        # self.cursor=self.conn.cursor()
        # return self 如此寫,這個session必須是一個線程級別的,若是用進程,則直接覆蓋cursor
        # #由於線程是順序執行的,都用新的cursor()當查詢數據時,數據找不到了,由於cursor變了。本session是在線程內執行,不能誇線程執行

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        if exc_type: # 此處用於定義是否出錯,若出錯,則直接返回
            self.conn.rollback()
        else:
            self.conn.commit()

class ModeMetd(type):  # 子類構建的時候實現的
    def __new__(cls,name,bases,attrs:dict):  # 此處是在類的定義中進行調用的,
        # 此處的name表示被調用子類的類名,而對應的字典attrs則表示子類的屬性字典
        # name 類名,attrs類屬性字典
        if '__table__'  not in attrs.keys():
            attrs['__table__']=name   #默認添加表名稱爲類名稱

        mapping={}  # 方便後面查詢屬性名和字段實例
        primarykey=[]  # 多個主鍵的狀況下使用,若是使用一個變量名,則致使後面覆蓋前面
        for k,v in attrs.items():  # k表明的是列名稱,v表示的是子類的類型
            if isinstance(v,Field):  # 此處判斷是否繼承與父類
                mapping[k]=v
                if  v.name is None:
                    v.name=k  # 若是是,則將類.name
                if  v.column is None:
                    v.column=v.name # 沒有給字段名,則使用類對應的列名稱
                if v.pk:
                    primarykey.append(v)
        # 增長屬性
        attrs['__mapping__']=mapping
        attrs['__primarykey__']=primarykey
        return super().__new__(cls,name,bases,attrs)

class  Model(metaclass=ModeMetd):  # 實體類的調用時實現,子類的實例調用時實現的
    pass

class  Login(Model):
    id=IntField(pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,nullable=False)
    age=IntField()
    __table__='Login'
    def  __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__
class  Engine:
    def __init__(self,*args,**kwargs):
        self.conn=pymysql.Connect(*args,**kwargs)
    def save(self, instance:Login):
        names = []
        values = []
        for k, v in instance.__mapping__.items():  # 此處用於獲取實例的名稱和其對應的值
            # print ('for',k,'---',v)
            if isinstance(v, Field):  # 此處若屬於基類
                if k in instance.__dict__.keys():  # 此處的字段符合
                    names.append(k)
                    values.append(instance.__dict__[k])  # v是一個Field類型的實例,是子類和父類的實例
                    print(instance.__dict__[k])  # 此處是實例。實例中的數字
        # __table__  # 此處在子類中添加
        query = "insert into {} ({}) values ({})".format(instance.__table__, ",".join(names),
                                                         ",".join(["%s"] * len(values)))  # 此處是匹配對應的sql
        print(query)
        print (values)

l=Login(1,'admin',20)
e=Engine('192.168.1.120','root','666666','test')
e.save(l)

基礎結果以下

ORM對象關係映射框架基本搭建

#!/usr/bin/poython3.6
#conding:utf-8
import  pymysql
class  Field:
    def __init__(self,name,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        self.name=name  # 字段名稱
        if  column  is None:  #列名稱
            self.column=self.name
        else:
            self.column=column
        self.pk=pk  # 主鍵
        self.unique=unique  #惟一
        self.index=index #索引
        self.nullable=nullable  #是否爲空
        self.default=default  # 默認是否爲空
    def  validate(self,value):  # 此處定義數據校驗方式,每種不一樣類型的校驗方式不一樣,所以應該在子類中分別實現
        raise  NotImplementedError  #基類不實現此功能

    def __get__(self, instance, owner): #此處用於定義描述器,此處當子類的類調用此屬性時,會返回對應的值
        # 此處的self表示父類的實例,instance表示子類的實例,owner表示子類的類
        # pass
        if instance is None:  #此處爲None表示子類未生成對應實例
            return self  # 此處的self表示實例本身
        return  instance.__dict__[self.name]  # 返回實例對應的字段名稱
    def __set__(self, instance, value): #此處用於定義數據描述器,用於子類實例調用時使用,用於返回對應的結果
        # instace 表示子類的實例,其相關的信息應該被存儲於子類實例中,
        self.validate(value)
        instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對應的屬性的值,及真實的數據

    def  __str__(self):
        return   "{} <{}>".format(self.__class__.__name__,self.name)  # 此處返回被調用的類名和實例名稱
    __repr__=__str__
# 定義整數類型的類型屬性
class  IntField(Field):  #多了自增屬性。
    def  __init__(self,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None,auto_increment=True):
        self.auto_increment=auto_increment
        super().__init__(name,column,pk,unique,index,nullable,default)

    def validate(self,value):
        if value is  None:
            if self.pk:  # 主鍵不能爲空,所以此處會報錯
                raise TypeError("{}:{}".format(self.name,value))
            if not self.nullable:  # 當定義了非空時,上述的值爲空,則報錯
                raise TypeError
        else:
            if not isinstance(value,int):  #若數據的類型爲非int,則報錯
                raise TypeError("{}  is  not  int,  It's {}".format(self.name,type(value)))

# 定義字符串的類型屬性
class  StringField(Field):  #定義字符串屬性類
    # 增長了字符串長度的定義
    def __init__(self,length=32,name=None,column=None,pk=False,unique=False,index=False,nullable=True,default=None):
        super().__init__(name,column,pk,unique,index,nullable,default)  #此處的屬性能夠繼承父類的屬性
        self.length=length  #此處用於定義字符串類型的長度
    def  validate(self,value):  #此處用於定義各自的屬性檢查,對數據進行屬性檢查
        if  value  is None: # 此處的None對應數據庫的null
            if self.pk:  # 若是數據是None,而其定義了主鍵,則會報錯,由於主鍵必須不能是Null,主鍵非空且惟一
                raise TypeError("{} is pk,not None".format(self.name))
            if not  self.nullable:  # 若是其是None,而定義的是非null,則會報錯
                raise TypeError("{}  is  not  null".format(self.name))
        else:
            if not isinstance(value,str):
                raise TypeError("{} should be string".format(self.name))
            if len(value) > self.length:  #真實的值大於規定的值,則會報錯
                raise ValueError("{} is to long value={}".format(self.name,value))

# 具體類的實現
class  Session:  #此處用以封裝連接,可在此處增長上下文支持
    def __init__(self,conn:pymysql.connections.Connection):
        self.conn=conn
        self.cursor=None

    def execute(self,query,*args):
        if self.cursor is None:
            self.cursor=self.conn.cursor()
        self.cursor.execute(query,*args)
    def __enter__(self): # 此處實現方式和
        return self.conn.cursor()
        # self.cursor=self.conn.cursor()
        # return self 如此寫,這個session必須是一個線程級別的,若是用進程,則直接覆蓋cursor
        # 由於線程是順序執行的,都用新的cursor()當查詢數據時,數據找不到了,由於cursor變了。本session是在線程內執行,不能誇線程執行
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        if exc_type: # 此處用於定義是否出錯,若出錯,則直接返回
            self.conn.rollback()
        else:
            self.conn.commit()

class ModeMetd(type):  # 子類構建的時候實現的
    def __new__(cls,name,bases,attrs:dict):  # 此處是在類的定義中進行調用的,
        # 此處的name表示被調用子類的類名,而對應的字典attrs則表示子類的屬性字典
        # name 類名,attrs類屬性字典
        if '__table__'  not in attrs.keys():
            attrs['__table__']=name   #默認添加表名稱爲類名稱

        mapping={}  # 方便後面查詢屬性名和字段實例
        primarykey=[]  # 多個主鍵的狀況下使用,若是使用一個變量名,則致使後面覆蓋前面
        for k,v in attrs.items():  # k表明的是列名稱,v表示的是子類的類型
            if isinstance(v,Field):  # 此處判斷是否繼承與父類
                mapping[k]=v
                if  v.name is None:
                    v.name=k  # 若是是,則將類.name
                if  v.column is None:
                    v.column=v.name # 沒有給字段名,則使用類對應的列名稱
                if v.pk:
                    primarykey.append(v)
        # 增長屬性
        attrs['__mapping__']=mapping
        attrs['__primarykey__']=primarykey
        return super().__new__(cls,name,bases,attrs)

class  Model(metaclass=ModeMetd):  # 實體類的調用時實現,子類的實例調用時實現的
    pass

class  Login(Model):
    id=IntField(pk=True,nullable=False,auto_increment=True)
    name=StringField(length=64,nullable=False)
    age=IntField()
    __table__='login'
    def  __init__(self,id,name,age):
        self.id=id
        self.name=name
        self.age=age
    def __str__(self):
        return   "Loin({},{},{})".format(self.id,self.name,self.age)
    __repr__=__str__
class  Engine:
    def __init__(self,*args,**kwargs):
        self.conn=pymysql.connections.Connection(*args,**kwargs)
    def save(self, instance:Login):
        names = []
        values = []
        for k, v in instance.__mapping__.items():  # 此處用於獲取實例的名稱和其對應的值
            # print ('for',k,'---',v)
            if isinstance(v, Field):  # 此處若屬於基類
                if k in instance.__dict__.keys():  # 此處的字段符合
                    names.append(k)
                    values.append(instance.__dict__[k])  # v是一個Field類型的實例,是子類和父類的實例
        # __table__  # 此處在子類中添加
        query = "insert into {}({}) values({})".format(instance.__table__, ",".join(names),
                                                         ",".join(["%s"] * len(values)))  # 此處是匹配對應的sql
        S=Session(self.conn)
        with  S:
            S.execute(query,values)

e=Engine('192.168.1.200','root','Admin@Root123','test')
for  i in range(10):
    e.save(Login(i,'admin'+str(i),20+i))

結果以下

ORM對象關係映射框架基本搭建

相關文章
相關標籤/搜索