Python 元类实现ORM,,ORM概念ORM(O


ORM概念

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

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

技术图片

示例:

class User(父类省略):    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__方法才能被使用函数的方式执行。

class A(object):    def __call__(self):        print "__call__ be called"a = A()a()    # 输出:__call__ be called 

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

class ModelMetaclass(type):    def __new__(cls, name, bases, attrs):        mappings = dict()        # 判断是否需要保存        for k, v in attrs.items():            # 判断是否是指定的StringField或者IntegerField的实例对象            if isinstance(v, tuple):                print(‘Found mapping: %s ==> %s‘ % (k, v))                mappings[k] = v        # 删除这些已经在字典中存储的属性        for k in mappings.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 in kwargs.items():            setattr(self, name, value)    # 设置属性值    def save(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 in args:            # 判断入如果是数字类型            if isinstance(temp, int):                args_temp.append(str(temp))            elif isinstance(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‘)

抽取到基类中

class ModelMetaclass(type):    def __new__(cls, name, bases, attrs):        mappings = dict()        # 判断是否需要保存        for k, v in attrs.items():            # 判断是否是指定的StringField或者IntegerField的实例对象            if isinstance(v, tuple):                print(‘Found mapping: %s ==> %s‘ % (k, v))                mappings[k] = v        # 删除这些已经在字典中存储的属性        for k in mappings.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 in kwargs.items():            setattr(self, name, value)  # 设置属性值    def save(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 in args:            # 判断入如果是数字类型            if isinstance(temp, int):                args_temp.append(str(temp))            elif isinstance(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):    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‘)# 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‘)

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

Python 元类实现ORM

评论关闭