Python 元类实现ORM

Posted 不自在

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 元类实现ORM相关的知识,希望对你有一定的参考价值。

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)  # 将name,value设置为实例属性

    def save(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 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):      # 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。

以上是关于Python 元类实现ORM的主要内容,如果未能解决你的问题,请参考以下文章

Python 元类实现ORM

35.Python面向对象元类:type()__metaclass__属性实现简易ORM框架

35.Python面向对象元类:type()__metaclass__属性实现简易ORM框架

35.Python面向对象元类:type()__metaclass__属性实现简易ORM框架

元类实现ORM

Python之元类ORM