Python元类编程实现一个简单的ORM

 更新时间:2023年03月05日 09:24:14   作者:代码输入中...  
本文主要介绍了Python元类编程实现一个简单的ORM,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

概述

什么是ORM?   

ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

现在我们就要实现简易版ORM。 

效果

class Person(Model):
    """
    定义类的属性到列的映射
    """
    pid = IntegerField('id')
    names = StringField('username')
    email = StringField('email')
    password = StringField('password')
 
p = Person(pid=10086, names='晓明', email='10086@163.com', password='123456')
p.save()

通过执行save()方法 动态生成sql插入语句, 是不是很神奇, 那我们现在开始解析原理吧

步骤

首先我们要定义一个 Field 类 它负责保存数据库表的字段名和字段类型:

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type
    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

在 Field 的基础上,进一步定义各种类型的 Field,比如 StringField,IntegerField 等等:

class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')
 
class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

下一步,就是编写最复杂的 ModelMetaclass:

class ModelMetaclass(type):
 
    def __new__(cls, name, bases, attrs):
        if name == "Model":
            return type.__new__(cls, name, bases, attrs)
        mappings = dict()
        print("Found class: %s" % name)
        for k, v in attrs.items():
            if isinstance(v, Field):
                print("Found mapping: %s ==> %s" % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs["__table__"] = name  # 表名和类名一致
        attrs["__mappings__"] = mappings  # 保存属性和列的映射关系
        return type.__new__(cls, name, bases, attrs)

最后就是基类  Model:

class Model(metaclass=ModelMetaclass):
 
    def __init__(self, **kwargs):
        _setattr = setattr
        if kwargs:
            for k, v in kwargs.items():
                _setattr(self, k, v)
        super(Model, self).__init__()
 
    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(k)
            params.append("?")
            args.append(getattr(self, k, None))
        sql = "insert into %s (%s) values (%s)" % (self.__table__, ','.join(fields), ",".join(params))
        print('插入语句: %s' % sql)
        print('参数: %s' % str(args))
 
    def update(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            if getattr(self, k, None):
                fields.append(k+"=?")
                args.append(getattr(self, k, None))
        sql = "update %s set %s" % (self.__table__, ','.join(fields))
        print("更新语句: %s " % sql)
        print("参数: %s" % args)
 
    def filter(self, *args):
        pass
 
    def delete(self):
        pass

当用户定义一个 class Person(Model) 继承父类时,Python解释器会在当前类 Person 的定义中找 __metaclass__,如果没有找到,就继续到父类中找 __metaclass__,实在找不到就用默认 type 类。

我们在父类 Model 中定义了 __metaclass__ 的 ModelMetaclass 来创建 Person 类,所以 metaclass 隐式地继承到子类。

在 ModelMetaclass 中,一共做了几件事情:

  • 排除掉对 Model 类的修改;
  • 在当前类(比如 Person )中查找定义的类的所有属性,如果找到一个 Field 属性,就把它保存到一个 __mappings__ 的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误;
  • 把表名保存到 __table__ 中,这里简化为表名默认为类名。

在Model类中,就可以定义各种操作数据库的方法,比如save(),delete(),find(),update() 等等。

我们实现了save(), update()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句和UPDATE语句。

编写代码试试:

class UserInfo(Model):
    """
        定义类的属性到列的映射
    """
    uid = IntegerField('uid')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')
 
 
class Person(Model):
    """
    定义类的属性到列的映射
    """
    pid = IntegerField('id')
    names = StringField('username')
    email = StringField('email')
    password = StringField('password')
 
p = Person(pid=10086, names='晓明', email='10086@163.com', password='123456')
p.save()
u2 = UserInfo(password='123456')
u2.update()

输出

Found class: UserInfo
Found mapping: uid ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found class: Person
Found mapping: pid ==> <IntegerField:id>
Found mapping: names ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
插入语句: insert into Person (pid,names,email,password) values (?,?,?,?)
参数: [10086, '晓明', '10086@163.com', '123456']
更新语句: update UserInfo set password=? 
参数: ['123456']

结束语

就这样一个小巧的ORM就这么完成了。是不是学到了很多呢 ?这里利用的是元编程,很多Python框架都运用了元编程达到动态操作类。

注:上述代码列子 结合了廖雪峰的列子和少量的django ORM源码。

完整代码

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type
 
    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)
 
 
class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')
 
 
class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')
 
 
class ModelMetaclass(type):
 
    def __new__(cls, name, bases, attrs):
        if name == "Model":
            return type.__new__(cls, name, bases, attrs)
        mappings = dict()
        print("Found class: %s" % name)
        for k, v in attrs.items():
            if isinstance(v, Field):
                print("Found mapping: %s ==> %s" % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs["__table__"] = name  # 表名和类名一致
        attrs["__mappings__"] = mappings  # 保存属性和列的映射关系
        return type.__new__(cls, name, bases, attrs)
 
 
class Model(metaclass=ModelMetaclass):
 
    def __init__(self, **kwargs):
        _setattr = setattr
        if kwargs:
            for k, v in kwargs.items():
                _setattr(self, k, v)
        super(Model, self).__init__()
 
    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(k)
            params.append("?")
            args.append(getattr(self, k, None))
        sql = "insert into %s (%s) values (%s)" % (self.__table__, ','.join(fields), ",".join(params))
        print('插入语句: %s' % sql)
        print('参数: %s' % str(args))
 
    def update(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            if getattr(self, k, None):
                fields.append(k+"=?")
                args.append(getattr(self, k, None))
        sql = "update %s set %s" % (self.__table__, ','.join(fields))
        print("更新语句: %s " % sql)
        print("参数: %s" % args)
 
    def filter(self, *args):
        pass
 
    def delete(self):
        pass
 
 
class UserInfo(Model):
    """
        定义类的属性到列的映射
    """
    uid = IntegerField('uid')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')
 
 
class Person(Model):
    """
    定义类的属性到列的映射
    """
    pid = IntegerField('id')
    names = StringField('username')
    email = StringField('email')
    password = StringField('password')
 
p = Person(pid=10086, names='晓明', email='10086@163.com', password='123456')
p.save()
u2 = UserInfo(password='123456')
u2.update()

到此这篇关于Python元类编程实现一个简单的ORM的文章就介绍到这了,更多相关Python元类编程实现ORM内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅析python 定时拆分备份 nginx 日志的方法

    浅析python 定时拆分备份 nginx 日志的方法

    本文给大家分享python 定时拆分备份 nginx 日志的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2020-04-04
  • python中metaclass原理与用法详解

    python中metaclass原理与用法详解

    这篇文章主要介绍了python中metaclass原理与用法,结合具体实例形式分析了Python中metaclass的功能、原理及使用metaclass动态创建类相关操作技巧,需要的朋友可以参考下
    2019-06-06
  • python2.6.6如何升级到python2.7.14

    python2.6.6如何升级到python2.7.14

    这篇文章主要为大家详细介绍了python2.6.6如何升级到python2.7.14,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • python连接打印机实现打印文档、图片、pdf文件等功能

    python连接打印机实现打印文档、图片、pdf文件等功能

    这篇文章主要介绍了python连接打印机实现打印文档、图片、pdf文件等功能,需要的朋友可以参考下
    2020-02-02
  • Python matplotlib绘制图形实例(包括点,曲线,注释和箭头)

    Python matplotlib绘制图形实例(包括点,曲线,注释和箭头)

    这篇文章主要介绍了Python matplotlib绘制图形实例(包括点,曲线,注释和箭头),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-04-04
  • 基于python实现新春烟花盛宴效果

    基于python实现新春烟花盛宴效果

    这篇文章给大家用Python绽放了一场新春烟花盛宴,这里提前祝大家新春快乐呀,文中通过代码示例给大家介绍的非常详细,感兴趣的小伙伴可以自己动手尝试一下
    2024-02-02
  • 基于python全局设置id 自动化测试元素定位过程解析

    基于python全局设置id 自动化测试元素定位过程解析

    这篇文章主要介绍了基于python全局设置id 自动化测试元素定位过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • python类特殊方法使用示例讲解

    python类特殊方法使用示例讲解

    这篇文章主要为大家介绍了python类特殊方法使用示例讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Python中的请求重试策略深入探讨

    Python中的请求重试策略深入探讨

    在网络通信中,由于各种原因,请求可能会失败,为了增加程序的健壮性和可靠性,实现一个优雅的请求重试策略是至关重要的,本文将深入探讨如何在Python中实现优雅的请求重试,通过丰富的示例代码和详细的解释,帮助大家更好地理解和应用重试机制
    2024-01-01
  • Python实现C#代码生成器应用服务于Unity示例解析

    Python实现C#代码生成器应用服务于Unity示例解析

    为了满足项目需要,需要实现一个c#代码生成器,为此设计了一个语法模板适用于Unity的代码生成器。本次使用了Python的Template模板,使用python开发
    2021-10-10

最新评论