ORM :對(duì)象關(guān)系映射,對(duì)象和關(guān)系之間的映射,使用面向?qū)ο蟮姆绞絹?lái)操作數(shù)據(jù)庫(kù)
創(chuàng)新互聯(lián)建站主要從事網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)久治,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):18980820575
關(guān)系對(duì)象模型和python對(duì)象模型之間的映射
tabel => class ,表映射類
row => object ,行映射為實(shí)例
column=> property ,字段映射屬性
表有l(wèi)ogin,字段為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,默認(rèn)值default,是否自增等,這些都是字段的特征,所以字段可以使用類來(lái)描述。
字段類要提供對(duì)數(shù)據(jù)的校驗(yàn)功能,如聲明字段類型為int類型,應(yīng)該要判斷數(shù)據(jù)是不是整數(shù)類形。
字段有多種類型,不同類型有差異,使用繼承的方式實(shí)現(xiàn)。
字段現(xiàn)在定義為類屬性,而這個(gè)類屬性又適合使用類來(lái)描述,這就是描述器了。
1 定義基類,用于實(shí)現(xiàn)所有類的基礎(chǔ)類型
#!/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 # 默認(rèn)是否為空
def validate(self,value): # 此處定義數(shù)據(jù)校驗(yàn)方式,每種不同類型的校驗(yàn)方式不同,因此應(yīng)該在子類中分別實(shí)現(xiàn)
raise NotImplementedError #基類不實(shí)現(xiàn)此功能
def __get__(self, instance, owner): #此處用于定義描述器,此處當(dāng)子類的類調(diào)用此屬性時(shí),會(huì)返回對(duì)應(yīng)的值
# 此處的self表示父類的實(shí)例,instance表示子類的實(shí)例,owner表示子類的類
# pass
if instance is None: #此處為None表示子類未生成對(duì)應(yīng)實(shí)例
return self # 此處的self表示實(shí)例自己
return instance.__dict__[self.name] # 返回實(shí)例對(duì)應(yīng)的字段名稱
def __set__(self, instance, value): #此處用于定義數(shù)據(jù)描述器,用于子類實(shí)例調(diào)用時(shí)使用,用于返回對(duì)應(yīng)的結(jié)果
# instace 表示子類的實(shí)例,其相關(guān)的信息應(yīng)該被存儲(chǔ)于子類實(shí)例中,
self.validate(value)
instance.__dict__[self.name]=value
def __str__(self):
return "{} <{}>".format(self.__class__.__name__,self.name) # 此處返回被調(diào)用的類名和實(shí)例名稱
__repr__=__str__
# 定義整數(shù)類型的類型屬性
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: # 主鍵不能為空,因此此處會(huì)報(bào)錯(cuò)
raise TypeError("{}:{}".format(self.name,value))
if not self.nullable: # 當(dāng)定義了非空時(shí),上述的值為空,則報(bào)錯(cuò)
raise TypeError
else:
if not isinstance(value,int): #若數(shù)據(jù)的類型為非int,則報(bào)錯(cuò)
raise TypeError("{} is not int, It's {}".format(self.name,type(value)))
# 定義字符串的類型屬性
class StringField(Field): #定義字符串屬性類
# 增加了字符串長(zhǎng)度的定義
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 #此處用于定義字符串類型的長(zhǎng)度
def validate(self,value): #此處用于定義各自的屬性檢查,對(duì)數(shù)據(jù)進(jìn)行屬性檢查
if value is None: # 此處的None對(duì)應(yīng)數(shù)據(jù)庫(kù)的null
if self.pk: # 如果數(shù)據(jù)是None,而其定義了主鍵,則會(huì)報(bào)錯(cuò),因?yàn)橹麈I必須不能是Null,主鍵非空且唯一
raise TypeError("{} is pk,not None".format(self.name))
if not self.nullable: # 如果其是None,而定義的是非null,則會(huì)報(bào)錯(cuò)
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: #真實(shí)的值大于規(guī)定的值,則會(huì)報(bào)錯(cuò)
raise ValueError("{} is to long value={}".format(self.name,value))
# 具體類的實(shí)現(xiàn)
class Login:
id=IntField('id','id',pk=True,nullable=False,auto_increment=True) # 此種調(diào)用方式會(huì)啟動(dòng)get方法的調(diào)用,從而返回實(shí)例自己
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類的操作對(duì)應(yīng)表的CRUD操作,及增刪改查,如果使用pyMySQL,應(yīng)該使用cursor對(duì)象的execute方法,增加數(shù)據(jù),修改數(shù)據(jù)定義為save方法,為L(zhǎng)ogin類增加此方法,數(shù)據(jù)庫(kù)的鏈接要求從外面?zhèn)魅?/p>具體實(shí)現(xiàn)如下
# 具體類的實(shí)現(xiàn)
# 具體類的實(shí)現(xiàn)
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))
每一次數(shù)據(jù)庫(kù)操作都是在一個(gè)會(huì)話中完成的,將cursor的操作封裝在會(huì)話中
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): # 此處實(shí)現(xiàn)方式和
return self.conn.cursor()
# self.cursor=self.conn.cursor()
# return self 如此寫,這個(gè)session必須是一個(gè)線程級(jí)別的,如果用進(jìn)程,則直接覆蓋cursor
# #因?yàn)榫€程是順序執(zhí)行的,都用新的cursor()當(dāng)查詢數(shù)據(jù)時(shí),數(shù)據(jù)找不到了,因?yàn)閏ursor變了。本session是在線程內(nèi)執(zhí)行,不能夸線程執(zhí)行
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
if exc_type: # 此處用于定義是否出錯(cuò),若出錯(cuò),則直接返回
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: # 此處直接調(diào)用enter返回cursor游標(biāo),此處的最后會(huì)關(guān)閉游標(biāo),但不會(huì)關(guān)閉鏈接
with cursor: # 此處使用游標(biāo)的屬性進(jìn)行處理
cursor.execute(sql,(self.id,self.name,self.age))
Login 這樣的類,如果多建立幾個(gè),其都是一個(gè)樣子,每一個(gè)這樣的類,得定義一個(gè)名稱對(duì)應(yīng)不同的表,都需要先定義好類,再定義__init__初始化值,而這些值剛好是定義好的類屬性,操作也是一樣的。CRID
設(shè)計(jì),定義一個(gè)Model類,增加一個(gè)__table__類屬性來(lái)保存不同的表名稱
class Model:
def save(self,session:Session=None):
names=[]
values=[]
for k,v in self.__class__.__dict__.items(): # 此處用于獲取實(shí)例的名稱和其對(duì)應(yīng)的值
# 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))) # 此處是匹配對(duì)應(yīng)的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__
編寫一個(gè)元類ModelMeta
以它作為元類的類,都可以獲得一個(gè)類屬性
如果沒(méi)有定義_table_,就自動(dòng)加上這個(gè)屬性,值為類名
可以遍歷類屬性,找出定義的字段類,建立一張映射表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 # 默認(rèn)是否為空
def validate(self,value): # 此處定義數(shù)據(jù)校驗(yàn)方式,每種不同類型的校驗(yàn)方式不同,因此應(yīng)該在子類中分別實(shí)現(xiàn)
raise NotImplementedError #基類不實(shí)現(xiàn)此功能
def __get__(self, instance, owner): #此處用于定義描述器,此處當(dāng)子類的類調(diào)用此屬性時(shí),會(huì)返回對(duì)應(yīng)的值
# 此處的self表示父類的實(shí)例,instance表示子類的實(shí)例,owner表示子類的類
# pass
if instance is None: #此處為None表示子類未生成對(duì)應(yīng)實(shí)例
return self # 此處的self表示實(shí)例自己
return instance.__dict__[self.name] # 返回實(shí)例對(duì)應(yīng)的字段名稱
def __set__(self, instance, value): #此處用于定義數(shù)據(jù)描述器,用于子類實(shí)例調(diào)用時(shí)使用,用于返回對(duì)應(yīng)的結(jié)果
# instace 表示子類的實(shí)例,其相關(guān)的信息應(yīng)該被存儲(chǔ)于子類實(shí)例中,
self.validate(value)
instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對(duì)應(yīng)的屬性的值,及真實(shí)的數(shù)據(jù)
def __str__(self):
return "{} <{}>".format(self.__class__.__name__,self.name) # 此處返回被調(diào)用的類名和實(shí)例名稱
__repr__=__str__
# 定義整數(shù)類型的類型屬性
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: # 主鍵不能為空,因此此處會(huì)報(bào)錯(cuò)
raise TypeError("{}:{}".format(self.name,value))
if not self.nullable: # 當(dāng)定義了非空時(shí),上述的值為空,則報(bào)錯(cuò)
raise TypeError
else:
if not isinstance(value,int): #若數(shù)據(jù)的類型為非int,則報(bào)錯(cuò)
raise TypeError("{} is not int, It's {}".format(self.name,type(value)))
# 定義字符串的類型屬性
class StringField(Field): #定義字符串屬性類
# 增加了字符串長(zhǎng)度的定義
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 #此處用于定義字符串類型的長(zhǎng)度
def validate(self,value): #此處用于定義各自的屬性檢查,對(duì)數(shù)據(jù)進(jìn)行屬性檢查
if value is None: # 此處的None對(duì)應(yīng)數(shù)據(jù)庫(kù)的null
if self.pk: # 如果數(shù)據(jù)是None,而其定義了主鍵,則會(huì)報(bào)錯(cuò),因?yàn)橹麈I必須不能是Null,主鍵非空且唯一
raise TypeError("{} is pk,not None".format(self.name))
if not self.nullable: # 如果其是None,而定義的是非null,則會(huì)報(bào)錯(cuò)
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: #真實(shí)的值大于規(guī)定的值,則會(huì)報(bào)錯(cuò)
raise ValueError("{} is to long value={}".format(self.name,value))
# 具體類的實(shí)現(xiàn)
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): # 此處實(shí)現(xiàn)方式和
return self.conn.cursor()
# self.cursor=self.conn.cursor()
# return self 如此寫,這個(gè)session必須是一個(gè)線程級(jí)別的,如果用進(jìn)程,則直接覆蓋cursor
# #因?yàn)榫€程是順序執(zhí)行的,都用新的cursor()當(dāng)查詢數(shù)據(jù)時(shí),數(shù)據(jù)找不到了,因?yàn)閏ursor變了。本session是在線程內(nèi)執(zhí)行,不能夸線程執(zhí)行
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
if exc_type: # 此處用于定義是否出錯(cuò),若出錯(cuò),則直接返回
self.conn.rollback()
else:
self.conn.commit()
class ModeMetd(type): # 子類構(gòu)建的時(shí)候?qū)崿F(xiàn)的
def __new__(cls,name,bases,attrs:dict): # 此處是在類的定義中進(jìn)行調(diào)用的,
# 此處的name表示被調(diào)用子類的類名,而對(duì)應(yīng)的字典attrs則表示子類的屬性字典
# name 類名,attrs類屬性字典
if '__table__' not in attrs.keys():
attrs['__table__']=name #默認(rèn)添加表名稱為類名稱
mapping={} # 方便后面查詢屬性名和字段實(shí)例
primarykey=[] # 多個(gè)主鍵的情況下使用,如果使用一個(gè)變量名,則導(dǎo)致后面覆蓋前面
for k,v in attrs.items(): # k代表的是列名稱,v表示的是子類的類型
if isinstance(v,Field): # 此處判斷是否繼承與父類
mapping[k]=v
if v.name is None: # 此處用于處理子類的屬性的字典的列名稱處理問(wèn)題
v.name=k # 如果是,則將類.name
if v.column is None:
v.column=v.name # 沒(méi)有給字段名,則使用類對(duì)應(yīng)的列名稱
if v.pk:
primarykey.append(v)
# 增加屬性
attrs['__mapping__']=mapping
attrs['__primarykey__']=primarykey
return super().__new__(cls,name,bases,attrs)
class Model(metaclass=ModeMetd): # 實(shí)體類的調(diào)用時(shí)實(shí)現(xiàn),子類的實(shí)例調(diào)用時(shí)實(shí)現(xiàn)的
def save(self,session:Session=None):
names=[]
values=[]
for k,v in self.__class__.__dict__.items(): # 此處用于獲取實(shí)例的名稱和其對(duì)應(yīng)的值
# print ('for',k,'---',v)
if isinstance(v,Field): # 此處若屬于基類
if k in self.__dict__.keys(): # 此處的字段符合
names.append(k)
values.append(self.__dict__[k]) # v是一個(gè)Field類型的實(shí)例,是子類和父類的實(shí)例
print (self.__dict__[k]) # 此處是實(shí)例。實(shí)例中的數(shù)字
# __table__ # 此處在子類中添加
query="insert into {} ({}) values ({})".format(self.__table__,",".join(names),",".join(["%s"]*len(values))) # 此處是匹配對(duì)應(yīng)的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
結(jié)果如下
實(shí)體類沒(méi)有提供數(shù)據(jù)庫(kù)連接,當(dāng)然也不應(yīng)該提供,實(shí)體類就應(yīng)該只完成表和類的映射。
提供一個(gè)數(shù)據(jù)庫(kù)的包裝類
1 負(fù)責(zé)數(shù)據(jù)庫(kù)連接
2 負(fù)責(zé)CRUD操作,取代實(shí)體類的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 # 默認(rèn)是否為空
def validate(self,value): # 此處定義數(shù)據(jù)校驗(yàn)方式,每種不同類型的校驗(yàn)方式不同,因此應(yīng)該在子類中分別實(shí)現(xiàn)
raise NotImplementedError #基類不實(shí)現(xiàn)此功能
def __get__(self, instance, owner): #此處用于定義描述器,此處當(dāng)子類的類調(diào)用此屬性時(shí),會(huì)返回對(duì)應(yīng)的值
# 此處的self表示父類的實(shí)例,instance表示子類的實(shí)例,owner表示子類的類
# pass
if instance is None: #此處為None表示子類未生成對(duì)應(yīng)實(shí)例
return self # 此處的self表示實(shí)例自己
return instance.__dict__[self.name] # 返回實(shí)例對(duì)應(yīng)的字段名稱
def __set__(self, instance, value): #此處用于定義數(shù)據(jù)描述器,用于子類實(shí)例調(diào)用時(shí)使用,用于返回對(duì)應(yīng)的結(jié)果
# instace 表示子類的實(shí)例,其相關(guān)的信息應(yīng)該被存儲(chǔ)于子類實(shí)例中,
self.validate(value)
instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對(duì)應(yīng)的屬性的值,及真實(shí)的數(shù)據(jù)
def __str__(self):
return "{} <{}>".format(self.__class__.__name__,self.name) # 此處返回被調(diào)用的類名和實(shí)例名稱
__repr__=__str__
# # 定義整數(shù)類型的類型屬性
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: # 主鍵不能為空,因此此處會(huì)報(bào)錯(cuò)
raise TypeError("{}:{}".format(self.name,value))
if not self.nullable: # 當(dāng)定義了非空時(shí),上述的值為空,則報(bào)錯(cuò)
raise TypeError
else:
if not isinstance(value,int): #若數(shù)據(jù)的類型為非int,則報(bào)錯(cuò)
raise TypeError("{} is not int, It's {}".format(self.name,type(value)))
# 定義字符串的類型屬性
class StringField(Field): #定義字符串屬性類
# 增加了字符串長(zhǎng)度的定義
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 #此處用于定義字符串類型的長(zhǎng)度
def validate(self,value): #此處用于定義各自的屬性檢查,對(duì)數(shù)據(jù)進(jìn)行屬性檢查
if value is None: # 此處的None對(duì)應(yīng)數(shù)據(jù)庫(kù)的null
if self.pk: # 如果數(shù)據(jù)是None,而其定義了主鍵,則會(huì)報(bào)錯(cuò),因?yàn)橹麈I必須不能是Null,主鍵非空且唯一
raise TypeError("{} is pk,not None".format(self.name))
if not self.nullable: # 如果其是None,而定義的是非null,則會(huì)報(bào)錯(cuò)
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: #真實(shí)的值大于規(guī)定的值,則會(huì)報(bào)錯(cuò)
raise ValueError("{} is to long value={}".format(self.name,value))
# 具體類的實(shí)現(xiàn)
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): # 此處實(shí)現(xiàn)方式和
return self.conn.cursor()
# self.cursor=self.conn.cursor()
# return self 如此寫,這個(gè)session必須是一個(gè)線程級(jí)別的,如果用進(jìn)程,則直接覆蓋cursor
# #因?yàn)榫€程是順序執(zhí)行的,都用新的cursor()當(dāng)查詢數(shù)據(jù)時(shí),數(shù)據(jù)找不到了,因?yàn)閏ursor變了。本session是在線程內(nèi)執(zhí)行,不能夸線程執(zhí)行
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
if exc_type: # 此處用于定義是否出錯(cuò),若出錯(cuò),則直接返回
self.conn.rollback()
else:
self.conn.commit()
class ModeMetd(type): # 子類構(gòu)建的時(shí)候?qū)崿F(xiàn)的
def __new__(cls,name,bases,attrs:dict): # 此處是在類的定義中進(jìn)行調(diào)用的,
# 此處的name表示被調(diào)用子類的類名,而對(duì)應(yīng)的字典attrs則表示子類的屬性字典
# name 類名,attrs類屬性字典
if '__table__' not in attrs.keys():
attrs['__table__']=name #默認(rèn)添加表名稱為類名稱
mapping={} # 方便后面查詢屬性名和字段實(shí)例
primarykey=[] # 多個(gè)主鍵的情況下使用,如果使用一個(gè)變量名,則導(dǎo)致后面覆蓋前面
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 # 沒(méi)有給字段名,則使用類對(duì)應(yīng)的列名稱
if v.pk:
primarykey.append(v)
# 增加屬性
attrs['__mapping__']=mapping
attrs['__primarykey__']=primarykey
return super().__new__(cls,name,bases,attrs)
class Model(metaclass=ModeMetd): # 實(shí)體類的調(diào)用時(shí)實(shí)現(xiàn),子類的實(shí)例調(diào)用時(shí)實(shí)現(xiàn)的
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(): # 此處用于獲取實(shí)例的名稱和其對(duì)應(yīng)的值
# print ('for',k,'---',v)
if isinstance(v, Field): # 此處若屬于基類
if k in instance.__dict__.keys(): # 此處的字段符合
names.append(k)
values.append(instance.__dict__[k]) # v是一個(gè)Field類型的實(shí)例,是子類和父類的實(shí)例
print(instance.__dict__[k]) # 此處是實(shí)例。實(shí)例中的數(shù)字
# __table__ # 此處在子類中添加
query = "insert into {} ({}) values ({})".format(instance.__table__, ",".join(names),
",".join(["%s"] * len(values))) # 此處是匹配對(duì)應(yīng)的sql
print(query)
print (values)
l=Login(1,'admin',20)
e=Engine('192.168.1.120','root','666666','test')
e.save(l)
基礎(chǔ)結(jié)果如下
#!/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 # 默認(rèn)是否為空
def validate(self,value): # 此處定義數(shù)據(jù)校驗(yàn)方式,每種不同類型的校驗(yàn)方式不同,因此應(yīng)該在子類中分別實(shí)現(xiàn)
raise NotImplementedError #基類不實(shí)現(xiàn)此功能
def __get__(self, instance, owner): #此處用于定義描述器,此處當(dāng)子類的類調(diào)用此屬性時(shí),會(huì)返回對(duì)應(yīng)的值
# 此處的self表示父類的實(shí)例,instance表示子類的實(shí)例,owner表示子類的類
# pass
if instance is None: #此處為None表示子類未生成對(duì)應(yīng)實(shí)例
return self # 此處的self表示實(shí)例自己
return instance.__dict__[self.name] # 返回實(shí)例對(duì)應(yīng)的字段名稱
def __set__(self, instance, value): #此處用于定義數(shù)據(jù)描述器,用于子類實(shí)例調(diào)用時(shí)使用,用于返回對(duì)應(yīng)的結(jié)果
# instace 表示子類的實(shí)例,其相關(guān)的信息應(yīng)該被存儲(chǔ)于子類實(shí)例中,
self.validate(value)
instance.__dict__[self.name]=value #此處的name是字段名,value是子類的self,對(duì)應(yīng)的屬性的值,及真實(shí)的數(shù)據(jù)
def __str__(self):
return "{} <{}>".format(self.__class__.__name__,self.name) # 此處返回被調(diào)用的類名和實(shí)例名稱
__repr__=__str__
# 定義整數(shù)類型的類型屬性
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: # 主鍵不能為空,因此此處會(huì)報(bào)錯(cuò)
raise TypeError("{}:{}".format(self.name,value))
if not self.nullable: # 當(dāng)定義了非空時(shí),上述的值為空,則報(bào)錯(cuò)
raise TypeError
else:
if not isinstance(value,int): #若數(shù)據(jù)的類型為非int,則報(bào)錯(cuò)
raise TypeError("{} is not int, It's {}".format(self.name,type(value)))
# 定義字符串的類型屬性
class StringField(Field): #定義字符串屬性類
# 增加了字符串長(zhǎng)度的定義
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 #此處用于定義字符串類型的長(zhǎng)度
def validate(self,value): #此處用于定義各自的屬性檢查,對(duì)數(shù)據(jù)進(jìn)行屬性檢查
if value is None: # 此處的None對(duì)應(yīng)數(shù)據(jù)庫(kù)的null
if self.pk: # 如果數(shù)據(jù)是None,而其定義了主鍵,則會(huì)報(bào)錯(cuò),因?yàn)橹麈I必須不能是Null,主鍵非空且唯一
raise TypeError("{} is pk,not None".format(self.name))
if not self.nullable: # 如果其是None,而定義的是非null,則會(huì)報(bào)錯(cuò)
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: #真實(shí)的值大于規(guī)定的值,則會(huì)報(bào)錯(cuò)
raise ValueError("{} is to long value={}".format(self.name,value))
# 具體類的實(shí)現(xiàn)
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): # 此處實(shí)現(xiàn)方式和
return self.conn.cursor()
# self.cursor=self.conn.cursor()
# return self 如此寫,這個(gè)session必須是一個(gè)線程級(jí)別的,如果用進(jìn)程,則直接覆蓋cursor
# 因?yàn)榫€程是順序執(zhí)行的,都用新的cursor()當(dāng)查詢數(shù)據(jù)時(shí),數(shù)據(jù)找不到了,因?yàn)閏ursor變了。本session是在線程內(nèi)執(zhí)行,不能夸線程執(zhí)行
def __exit__(self, exc_type, exc_val, exc_tb):
self.cursor.close()
if exc_type: # 此處用于定義是否出錯(cuò),若出錯(cuò),則直接返回
self.conn.rollback()
else:
self.conn.commit()
class ModeMetd(type): # 子類構(gòu)建的時(shí)候?qū)崿F(xiàn)的
def __new__(cls,name,bases,attrs:dict): # 此處是在類的定義中進(jìn)行調(diào)用的,
# 此處的name表示被調(diào)用子類的類名,而對(duì)應(yīng)的字典attrs則表示子類的屬性字典
# name 類名,attrs類屬性字典
if '__table__' not in attrs.keys():
attrs['__table__']=name #默認(rèn)添加表名稱為類名稱
mapping={} # 方便后面查詢屬性名和字段實(shí)例
primarykey=[] # 多個(gè)主鍵的情況下使用,如果使用一個(gè)變量名,則導(dǎo)致后面覆蓋前面
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 # 沒(méi)有給字段名,則使用類對(duì)應(yīng)的列名稱
if v.pk:
primarykey.append(v)
# 增加屬性
attrs['__mapping__']=mapping
attrs['__primarykey__']=primarykey
return super().__new__(cls,name,bases,attrs)
class Model(metaclass=ModeMetd): # 實(shí)體類的調(diào)用時(shí)實(shí)現(xiàn),子類的實(shí)例調(diào)用時(shí)實(shí)現(xiàn)的
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(): # 此處用于獲取實(shí)例的名稱和其對(duì)應(yīng)的值
# print ('for',k,'---',v)
if isinstance(v, Field): # 此處若屬于基類
if k in instance.__dict__.keys(): # 此處的字段符合
names.append(k)
values.append(instance.__dict__[k]) # v是一個(gè)Field類型的實(shí)例,是子類和父類的實(shí)例
# __table__ # 此處在子類中添加
query = "insert into {}({}) values({})".format(instance.__table__, ",".join(names),
",".join(["%s"] * len(values))) # 此處是匹配對(duì)應(yīng)的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))
結(jié)果如下
網(wǎng)站欄目:ORM對(duì)象關(guān)系映射框架基本搭建
轉(zhuǎn)載來(lái)源:http://chinadenli.net/article34/giegpe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營(yíng)銷、標(biāo)簽優(yōu)化、定制網(wǎng)站、做網(wǎng)站、企業(yè)建站、外貿(mào)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)