文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么使用Python元类编程实现一个简单的ORM

2023-07-05 09:20

关注

这篇文章主要讲解了“怎么使用Python元类编程实现一个简单的ORM”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么使用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()

怎么使用Python元类编程实现一个简单的ORM

通过执行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类中,就可以定义各种操作数据库的方法,比如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这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯