Python ORM框架如何实现
这篇文章主要介绍了Python ORM框架如何实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Python ORM框架如何实现文章都会有所收获,下面我们一起来看看吧。
ORM是什么O是 object,也就 类对象 的意思,R是 relation,翻译成中文是 关系,也就是关系数据库中 数据表 的意思,M是 mapping,是映射的意思。在ORM框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据库中的表,省去了我们自己建表的过程。
一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应 MySQL 语句。
在 Django 中就内嵌了一个 ORM 框架,不需要直接面向数据库编程,而是定义模型类,通过模型类和对象完成数据表的增删改查操作。还有第三方库 sqlalchemy 都是 ORM框架。
先看看我们大致要实现什么功能
classUser(父类省略):uid=('uid',"intunsigned")name=('username',"varchar(30)")email=('email',"varchar(30)")password=('password',"varchar(30)")...省略...user=User(uid=123,name='hui',email='huidbk@163.com',password='123456')user.save()#对应如下sql语句#insertintoUser(uid,username,email,password)values(123,hui,huidbk@163.com,123456)
所谓的 ORM 就是让开发者在操作数据库的时候,能够像操作对象时通过xxxx.属性=yyyy一样简单,这是开发ORM的初衷。
实现ORM中的insert功能通过 Python 中 元类 简单实现 ORM 中的 insert 功能
#!/usr/bin/python3#-*-coding:utf-8-*-#@Author:Hui#@Desc:{利用Python元类简单实现ORM框架的Insert插入功能}#@Date:2021/05/1717:02classModelMetaclass(type):"""数据表模型元类"""def__new__(mcs,cls_name,bases,attrs):print(f'cls_name->{cls_name}')#类名print(f'bases->{bases}')#继承类print(f'attrs->{attrs}')#类中所有属性print()#数据表对应关系字典mappings=dict()#过滤出对应数据表的字段属性fork,vinattrs.items():#判断是否是指定的StringField或者IntegerField的实例对象#这里就简单判断字段是元组ifisinstance(v,tuple):print('Foundmapping:%s==>%s'%(k,v))mappings[k]=v#删除这些已经在字典中存储的字段属性forkinmappings.keys():attrs.pop(k)#将之前的uid/name/email/password以及对应的对象引用、类名字#用其他类属性名称保存attrs['__mappings__']=mappings#保存属性和列的映射关系attrs['__table__']=cls_name#假设表名和类名一致returntype.__new__(mcs,cls_name,bases,attrs)classUser(metaclass=ModelMetaclass):"""用户模型类"""#类属性名表字段表字段类型uid=('uid','intunsigned')name=('username','varchar(30)')email=('email','varchar(30)')password=('password','varchar(30)')def__init__(self,**kwargs):forname,valueinkwargs.items():setattr(self,name,value)defsave(self):fields=[]args=[]fork,vinself.__mappings__.items():fields.append(v[0])args.append(getattr(self,k,None))#表名table_name=self.__table__#数据表中的字段fields=','.join(fields)#待插入的数据args=','.join([str(i)foriinargs])#生成sql语句sql=f"""insertinto{table_name}({fields})values({args})"""print(f'SQL:{sql}')defmain():user=User(uid=123,name='hui',email='huidbk@163.com',password='123456')user.save()if__name__=='__main__':main()
当 User 指定元类之后,uid、name、email、password 类属性将不在类中,而是在 __mappings__ 属性指定的字典中存储。 User 类的这些属性将转变为如下
__mappings__={"uid":('uid',"intunsigned")"name":('username',"varchar(30)")"email":('email',"varchar(30)")"password":('password',"varchar(30)")}__table__="User"
执行的效果如下:
cls_name->Userbases->()attrs->{'__module__':'__main__','__qualname__':'User','__doc__':'用户模型类','uid':('uid','intunsigned'),'name':('username','varchar(30)'),'email':('email','varchar(30)'),'password':('password','varchar(30)'),'__init__':<functionUser.__init__at0x0000026D520C1048>,'save':<functionUser.saveat0x0000026D520C10D8>}Foundmapping:uid==>('uid','intunsigned')Foundmapping:name==>('username','varchar(30)')Foundmapping:email==>('email','varchar(30)')Foundmapping:password==>('password','varchar(30)')SQL:insertintoUser(uid,username,email,password)values(123,hui,huidbk@163.com,123456)完善对数据类型的检测
上面转成的 sql 语句如下:
insertintoUser(uid,username,email,password)values(12345,hui,huidbk@163.com,123456)
发现没有,在 sql 语句中字符串类型没有没有引号 ''
正确的 sql 语句应该是:
insertintoUser(uid,username,email,password)values(123,'hui','huidbk@163.com','123456')
因此修改 User 类完善数据类型的检测
classModelMetaclass(type):#此处和上文一样,故省略....passclassUser(metaclass=ModelMetaclass):"""用户模型类"""uid=('uid',"intunsigned")name=('username',"varchar(30)")email=('email',"varchar(30)")password=('password',"varchar(30)")def__init__(self,**kwargs):forname,valueinkwargs.items():setattr(self,name,value)#在这里完善数据类型检测defsave(self):fields=[]args=[]fork,vinself.__mappings__.items():fields.append(v[0])args.append(getattr(self,k,None))#把参数数据类型对应数据表的字段类型args_temp=list()fortempinargs:ifisinstance(temp,int):args_temp.append(str(temp))elifisinstance(temp,str):args_temp.append(f"'{temp}'")#表名table_name=self.__table__#数据表中的字段fields=','.join(fields)#待插入的数据args=','.join(args_temp)#生成sql语句sql=f"""insertinto{table_name}({fields})values({args})"""print(f'SQL:{sql}')defmain():user=User(uid=123,name='hui',email='huidbk@163.com',password='123456')user.save()if__name__=='__main__':main()
运行效果如下:
cls_name->Userbases->()attrs->{'__module__':'__main__','__qualname__':'User','__doc__':'用户模型类','uid':('uid','intunsigned'),'name':('username','varchar(30)'),'email':('email','varchar(30)'),'password':('password','varchar(30)'),'__init__':<functionUser.__init__at0x0000026D520C1048>,'save':<functionUser.saveat0x0000026D520C10D8>}Foundmapping:uid==>('uid','intunsigned')Foundmapping:name==>('username','varchar(30)')Foundmapping:email==>('email','varchar(30)')Foundmapping:password==>('password','varchar(30)')SQL:insertintoUser(uid,username,email,password)values(123,'hui','huidbk@163.com','123456')抽取到基类中
#!/usr/bin/python3#-*-coding:utf-8-*-#@Author:Hui#@Desc:{利用Python元类实现ORM框架的Insert插入功能}#@Date:2021/05/1717:02classModelMetaclass(type):"""数据表模型元类"""def__new__(mcs,cls_name,bases,attrs):print(f'cls_name->{cls_name}')#类名print(f'bases->{bases}')#继承类print(f'attrs->{attrs}')#类中所有属性print()#数据表对应关系字典mappings=dict()#过滤出对应数据表的字段属性fork,vinattrs.items():#判断是否是对应数据表的字段属性,因为attrs中包含所有的类属性#这里就简单判断字段是元组ifisinstance(v,tuple):print('Foundmapping:%s==>%s'%(k,v))mappings[k]=v#删除这些已经在字典中存储的字段属性forkinmappings.keys():attrs.pop(k)#将之前的uid/name/email/password以及对应的对象引用、类名字#用其他类属性名称保存attrs['__mappings__']=mappings#保存属性和列的映射关系attrs['__table__']=cls_name#假设表名和类名一致returntype.__new__(mcs,cls_name,bases,attrs)classModel(object,metaclass=ModelMetaclass):"""数据表模型基类"""def__init__(self,**kwargs):forname,valueinkwargs.items():setattr(self,name,value)defsave(self):fields=[]args=[]fork,vinself.__mappings__.items():fields.append(v[0])args.append(getattr(self,k,None))#把参数数据类型对应数据表的字段类型args_temp=list()fortempinargs:ifisinstance(temp,int):args_temp.append(str(temp))elifisinstance(temp,str):args_temp.append(f"'{temp}'")#表名table_name=self.__table__#数据表中的字段fields=','.join(fields)#待插入的数据args=','.join(args_temp)#生成sql语句sql=f"""insertinto{table_name}({fields})values({args})"""print(f'SQL:{sql}')#执行sql语句#...classUser(Model):"""用户表模型类"""uid=('uid',"intunsigned")name=('username',"varchar(30)")email=('email',"varchar(30)")password=('password',"varchar(30)")defmain():user=User(uid=123,name='hui',email='huidbk@163.com',password='123456')user.save()if__name__=='__main__':main()添加数据库驱动执行sql语句
这里我们使用 pymysql 数据库驱动,来执行 sql 语句
在 Model 类中新增一个 get_connection 的静态方法用于获取数据库连接
importpymysqlclassModel(object,metaclass=ModelMetaclass):"""数据表模型基类"""def__init__(self,**kwargs):forname,valueinkwargs.items():setattr(self,name,value)@staticmethoddefget_connection():"""获取数据库连接与数据游标:return:conn,cursor"""conn=pymysql.connect(database='testdb',host='localhost',port=3306,user='root',password='123456')returnconn,conn.cursor()defsave(self):fields=[]args=[]fork,vinself.__mappings__.items():fields.append(v[0])args.append(getattr(self,k,None))#把参数数据类型对应数据表的字段类型args_temp=list()fortempinargs:ifisinstance(temp,int):args_temp.append(str(temp))elifisinstance(temp,str):args_temp.append(f"'{temp}'")#表名table_name=self.__table__#数据表中的字段fields=','.join(fields)#待插入的数据args=','.join(args_temp)#生成sql语句sql=f"""insertinto{table_name}({fields})values({args})"""print(f'SQL:{sql}')#执行sql语句conn,cursor=self.get_connection()ret=cursor.execute(sql)print(ret)conn.commit()cursor.close()conn.close()添加数据库驱动执行sql语句
这里我们使用 pymysql 数据库驱动,来执行 sql 语句
在 Model 类中新增一个 get_connection 的静态方法用于获取数据库连接
importpymysqlclassModel(object,metaclass=ModelMetaclass):"""数据表模型基类"""def__init__(self,**kwargs):forname,valueinkwargs.items():setattr(self,name,value)@staticmethoddefget_connection():"""获取数据库连接与数据游标:return:conn,cursor"""conn=pymysql.connect(database='testdb',host='localhost',port=3306,user='root',password='123456')returnconn,conn.cursor()defsave(self):fields=[]args=[]fork,vinself.__mappings__.items():fields.append(v[0])args.append(getattr(self,k,None))#把参数数据类型对应数据表的字段类型args_temp=list()fortempinargs:ifisinstance(temp,int):args_temp.append(str(temp))elifisinstance(temp,str):args_temp.append(f"'{temp}'")#表名table_name=self.__table__#数据表中的字段fields=','.join(fields)#待插入的数据args=','.join(args_temp)#生成sql语句sql=f"""insertinto{table_name}({fields})values({args})"""print(f'SQL:{sql}')#执行sql语句conn,cursor=self.get_connection()ret=cursor.execute(sql)print(ret)conn.commit()cursor.close()conn.close()测试功能
准备数据库
先准备数据库 testdb 和 user 数据表
createdatabasetestdbcharset=utf8;usetestdb;createtableuser(uidintunsignedauto_incrementprimarykey,usernamevarchar(30)notnull,emailvarchar(30),passwordvarchar(30)notnull);
user 表结构如下
+----------+------------------+------+-----+---------+----------------+|Field|Type|Null|Key|Default|Extra|+----------+------------------+------+-----+---------+----------------+|uid|int(10)unsigned|NO|PRI|NULL|auto_increment||username|varchar(30)|NO||NULL|||email|varchar(30)|YES||NULL|||password|varchar(30)|NO||NULL||+----------+------------------+------+-----+---------+----------------+创建模型类测试
classUser(Model):"""用户表模型类"""uid=('uid',"intunsigned")name=('username',"varchar(30)")email=('email',"varchar(30)")password=('password',"varchar(30)")defmain():user=User(uid=1,name='hui',email='huidbk@163.com',password='123456')user.save()foriinrange(2,10):user=User(uid=i,name=f'name{i}',email=f'huidbk@16{i}.com',password=f'12345{i}')user.save()if__name__=='__main__':main()
查看数据库 user 表数据
mysql>select*fromuser;+-----+----------+----------------+----------+|uid|username|email|password|+-----+----------+----------------+----------+|1|hui|huidbk@163.com|123456||2|name2|huidbk@162.com|123452||3|name3|huidbk@163.com|123453||4|name4|huidbk@164.com|123454||5|name5|huidbk@165.com|123455||6|name6|huidbk@166.com|123456||7|name7|huidbk@167.com|123457||8|name8|huidbk@168.com|123458||9|name9|huidbk@169.com|123459|+-----+----------+----------------+----------+9rowsinset(0.00sec)
关于“Python ORM框架如何实现”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Python ORM框架如何实现”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。