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))
结果以下