ORM :對象關係映射,對象和關係之間的映射,使用面向對象的方式來操做數據庫 python
關係對象模型和python對象模型之間的映射 mysql
tabel => class ,表映射類
row => object ,行映射爲實例
column=> property ,字段映射屬性 sql
表有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
字段有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))
# 具體類的實現 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))
每一次數據庫操做都是在一個會話中完成的,將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))
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__
編寫一個元類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
結果以下
實體類沒有提供數據庫鏈接,固然也不該該提供,實體類就應該只完成表和類的映射。
提供一個數據庫的包裝類
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)
基礎結果以下
#!/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))
結果以下