Python元类实战,通过元类实现数据库ORM框架( 二 )


class Model(dict, metaclass=ModelMetaclass):def __init__(self, **kw):# 由于Model的基类是dict,所以创造Model的字段会被解析成dict的构造参数# 也就是说字段名和字段值的映射会存储在dict当中super(Model, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Model' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = valuedef save(self):fields = []params = []args = []for k, v in self.__mappings__.items():# fields存储字段名fields.append(v.name)# params填充问号params.append('?')# 获取字段的值args.append(getattr(self, k, None))sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))print('SQL: %s' % sql)print('ARGS: %s' % str(args))如果你看过之前的文章,对元类已经很熟悉了,那么这段代码对你来说应该不难理解 。元类搞定了,剩下的Model就更简单了 。按照规范,我们需要实现增删改查四个函数,但是这里我们只是为了展示,所以就只实现其中一个作为例子,其他几个都可以如法炮制 。
class Model(dict, metaclass=ModelMetaclass):def __init__(self, **kw):# 由于Model的基类是dict,所以创造Model的字段会被解析成dict的构造参数# 也就是说字段名和字段值的映射会存储在dict当中super(Model, self).__init__(**kw)def __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Model' object has no attribute '%s'" % key)def __setattr__(self, key, value):self[key] = valuedef save(self):fields = []params = []args = []for k, v in self.__mappings__.items():# fields存储字段名fields.append(v.name)# params填充问号params.append('?')# 获取字段的值args.append(getattr(self, k, None))sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))print('SQL: %s' % sql)print('ARGS: %s' % str(args))Model当中的save方法不难看懂,但是前面的几个方法看起来有些多余 。但实际上它们也很重要,这里有一个关键信息是Model类的父类是dict,我们在构建Model的时候传入的参数会被用来初始化一个dict 。所以我们创建数据实例的时候数据的名称和数据值的映射会被存储在dict当中,所以我们在save方法当中才会从self的attr当中获取字段的值 。并且我们在初始化User的时候,也必须要填写每个字段的名称,原因就在这里 。
最后我们来运行一下:

Python元类实战,通过元类实现数据库ORM框架

文章插图
 
从结果上来看,我们输出了User这个类的插入SQL以及它的字段的值 。只需要链接一下数据库,我们的这个ORM框架就可以真正投入使用了 。
 总结在整个ORM框架实现的过程当中,最重要的是我们对Model这个类创建了元类,但是真正应用的地方却是在Model的子类 。实际上在实际创建User类的时候,解释器会先搜索User内部是否定义了元类,如果没有,会上一层去往User的父类也就是Model类搜索元类,如果找到了元类,就会使用元类来创建User 。相当于元类被隐形地继承了下来,但是我们在使用子类的时候却感知不到 。
对于框架的使用者来说,也的确不需要了解框架内部的实现机制,只需要明白使用方法,照着使用就行了 。虽然元类的实现和理解很复杂,但是使用起来却很简单,这也是它的一个显著特点 。
最后,本文的代码示例源于廖雪峰老师的博客,向廖雪峰老师致敬 。想要查看廖老师博客原文的,请点击查看原文 。
 




推荐阅读