python元类 orm_Python 元类实现ORM

ORM概念

ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL  的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作实体对象的属性和方法。

一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句。

示例:

classUser(父类省略):

uid= ('uid', "int unsigned")

name= ('username', "varchar(30)")

email= ('email', "varchar(30)")

password= ('password', "varchar(30)")

...省略...

u= User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')

u.save()#对应如下sql语句#insert into User (username,email,password,uid)#values ('Michael','test@orm.org','my-pwd',12345)

__new__、__init__、__call__的介绍

在讲使用元类创建ORM之前,必须了解__new__这个内置方法的作用。

__new__方法负责创建一个实例对象,在对象被创建的时候调用该方法它是一个类方法。__new__方法在返回一个实例之后,会自动的调用__init__方法,对实例进行初始化。如果__new__方法不返回值,或者返回的不是实例,那么它就不会自动的去调用__init__方法。

__init__ 方法负责将该实例对象进行初始化,在对象被创建之后调用该方法,在__new__方法创建出一个实例后对实例属性进行初始化。__init__方法可以没有返回值。

__call__方法其实和类的创建过程和实例化没有多大关系了,定义了__call__方法才能被以函数的方式执行。

classA(object):def __call__(self):print "__call__ be called"a=A()

a()#输出:__call__ be called

通过元类简单实现ORM中的insert功能

classModelMetaclass(type):def __new__(cls, name, bases, attrs):

mappings=dict()#判断是否需要保存

for k, v inattrs.items():#判断是否是指定的StringField或者IntegerField的实例对象

ifisinstance(v, tuple):print('Found mapping: %s ==> %s' %(k, v))

mappings[k]=v#删除这些已经在字典中存储的属性

for k inmappings.keys():

attrs.pop(k)#将之前的uid/name/email/password以及对应的对象引用、类名字

attrs['__mappings__'] = mappings #保存属性和列的映射关系

attrs['__table__'] = name #假设表名和类名一致

return type.__new__(cls, name, bases, attrs)class User(metaclass=ModelMetaclass):

uid= ('uid', "int unsigned")

name= ('username', "varchar(30)")

email= ('email', "varchar(30)")

password= ('password', "varchar(30)")#当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储

#以上User类中有

#__mappings__ = {

#"uid": ('uid', "int unsigned")

#"name": ('username', "varchar(30)")

#"email": ('email', "varchar(30)")

#"password": ('password', "varchar(30)")

#}

#__table__ = "User"

def __init__(self, **kwargs):for name, value inkwargs.items():

setattr(self, name, value)#设置属性值

defsave(self):

fields=[]

args=[]for k, v in self.__mappings__.items():

fields.append(v[0])

args.append(getattr(self, k, None))

args_temp=list()for temp inargs:#判断入如果是数字类型

ifisinstance(temp, int):

args_temp.append(str(temp))elifisinstance(temp, str):

args_temp.append("""'%s'""" % temp) #处理字符串类型的数据

sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))print('SQL: %s' %sql)

u= User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')#print(u.__dict__)

u.save()

输出结果

Found mapping: uid ==> ('uid', 'int unsigned')

Found mapping: name==> ('username', 'varchar(30)')

Found mapping: email==> ('email', 'varchar(30)')

Found mapping: password==> ('password', 'varchar(30)')

SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

抽取到基类中

classModelMetaclass(type):def __new__(cls, name, bases, attrs):

mappings=dict()#判断是否需要保存

for k, v inattrs.items():#判断是否是指定的StringField或者IntegerField的实例对象

ifisinstance(v, tuple):print('Found mapping: %s ==> %s' %(k, v))

mappings[k]= v #将提取的内容存放在一个字典中

#删除这些已经在字典中存储的属性

for k inmappings.keys():

attrs.pop(k)#将之前的uid/name/email/password以及对应的对象引用、类名字

attrs['__mappings__'] = mappings #保存属性和列的映射关系

attrs['__table__'] = name #假设表名和类名一致

return type.__new__(cls, name, bases, attrs)class Model(object, metaclass=ModelMetaclass):def __init__(self, **kwargs):for name, value inkwargs.items():

setattr(self, name, value)#将name,value设置为实例属性

defsave(self):

fields=[]

args=[]for k, v in self.__mappings__.items():#fields.append(v[0]) # v为类属性的元组

fields.append(k) #如果使用v[0],那就是取元组中的uid、username等为数据库表中字段名,不利于理解,直接取类属性的变量名

args.append(getattr(self, k)) #k为类属性的变量名

args_temp=list()for temp inargs:#判断入如果是数字类型

ifisinstance(temp, int):

args_temp.append(str(temp))elifisinstance(temp, str):

args_temp.append("""'%s'""" %temp)

sql= 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))print('SQL: %s' %sql)class User(Model): #User对应数据库中的表名

#uid = ('uid', "int unsigned")

#username = ('name', "varchar(30)")

#email = ('email', "varchar(30)")

#password = ('password', "varchar(30)")

uid = ("int unsigned",) #数据库字操作的字段以这里的字段名为主,实例化传的变量名,主要用来匹配各个字段对应的值,可以多但不能少,否则报错

username = ("varchar(30)",)

email= ("varchar(30)",)

password= ("varchar(30)",)

u= User(uid=12345, username='Michael', email='test@orm.org', password='my-pwd', phone=12345678900)#print(u.__dict__)

u.save()

输出结果

Found mapping: uid ==> ('int unsigned',)

Found mapping: username==> ('varchar(30)',)

Found mapping: email==> ('varchar(30)',)

Found mapping: password==> ('varchar(30)',)

SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

通过上面的示例,我们可以看出用元类创建API是非常好的选择,使用元类的编写虽然很复杂,但使用者可以非常简洁的调用API。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值