python对象关系映射_ORM对象关系映射框架基本搭建

一 概念

1 概念

ORM :对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作数据库

关系对象模型和python对象模型之间的映射

tabel => class ,表映射类

row => object ,行映射为实例

column=> property ,字段映射属性

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,是否自增等,这些都是字段的特征,所以字段可以使用类来描述。

字段类要提供对数据的校验功能,如声明字段类型为int类型,应该要判断数据是不是整数类形。

字段有多种类型,不同类型有差异,使用继承的方式实现。

字段现在定义为类属性,而这个类属性又适合使用类来描述,这就是描述器了。

1 定义基类,用于实现所有类的基础类型

#!/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类增加此方法,数据库的链接要求从外面传入

具体实现如下

# 具体类的实现

# 具体类的实现

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

结果如下

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)

基础结果如下

#!/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))

结果如下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值