Python 元类编程实现一个简单的 ORM
创始人
2024-05-28 15:54:25
0

概述

什么是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 = nameself.column_type = column_typedef __str__(self):return '<%s:%s>' % (self.__class__.__name__, self.name)

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

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] = vfor 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 = setattrif 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):passdef delete(self):pass

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

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

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

  1. 排除掉对 Model 类的修改;

  2. 在当前类(比如 Person )中查找定义的类的所有属性,如果找到一个 Field 属性,就把它保存到一个 __mappings__ 的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误;

  3. 把表名保存到 __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 ==> 
Found mapping: name ==> 
Found mapping: email ==> 
Found mapping: password ==> 
Found class: Person
Found mapping: pid ==> 
Found mapping: names ==> 
Found mapping: email ==> 
Found mapping: 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 = nameself.column_type = column_typedef __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] = vfor 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 = setattrif 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):passdef delete(self):passclass 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()

相关内容

热门资讯

安卓9系统怎样应用分身,轻松实... 你有没有发现,手机里的APP越来越多,有时候一个APP里还要处理好多任务,分身功能简直就是救星啊!今...
获取安卓系统的ip地址,轻松获... 你有没有想过,你的安卓手机里隐藏着一个神秘的IP地址?没错,就是那个能让你在网络世界里找到自己的小秘...
LG彩电安卓系统升级,畅享智能... 你家的LG彩电是不是最近有点儿“闹别扭”,屏幕上时不时地跳出个升级提示?别急,今天就来给你详细说说这...
阴阳师安卓苹果系统,安卓与苹果... 亲爱的玩家们,你是否曾在深夜里,手握手机,沉浸在阴阳师的神秘世界?今天,就让我带你一起探索这款风靡全...
华为安卓系统区别在哪,独特创新... 你知道吗?最近手机圈里可是热闹非凡,尤其是华为的新动作,让很多人眼睛都瞪大了。没错,我说的就是华为自...
怎么重新刷安卓手机系统,深度解... 手机用久了,是不是感觉卡顿得厉害?别急,今天就来教你怎么重新刷安卓手机系统,让你的手机焕然一新,速度...
刷正版安卓系统教程,刷正版安卓... 你有没有想过,让你的安卓手机焕然一新,体验一把正版系统的魅力呢?别急,今天就来手把手教你如何刷正版安...
移动支撑系统安卓版,助力移动办... 你有没有发现,现在的生活越来越离不开手机了?无论是工作还是娱乐,手机几乎成了我们生活的必需品。而今天...
安卓怎么进win系统界面,安卓... 亲爱的安卓用户,你是否曾幻想过在安卓设备上直接体验Windows系统的魅力?别再羡慕那些Window...
incall可以升级安卓系统吗... 你有没有想过,你的手机是不是也能像电脑一样,时不时地来个系统升级呢?今天,咱们就来聊聊这个话题——i...
安卓系统带农历软件,尽享传统节... 你知道吗?现在智能手机上有个特别实用的功能,那就是农历显示。对于咱们中国人来说,农历可是有着深厚的历...
安卓系统资源占用高,揭秘原因与... 你有没有发现,你的安卓手机最近变得越来越慢了?是不是觉得打开一个应用都要等半天,甚至有时候还会卡死?...
安卓10的系统有哪些,功能升级... 你有没有发现,你的安卓手机最近是不是变得有点不一样了?没错,就是那个神秘的安卓10系统!它就像一位魔...
固态硬盘系统迁移到安卓,固态硬... 你有没有想过,把你的固态硬盘系统迁移到安卓设备上,是不是能让你在移动办公或者娱乐时更加得心应手呢?想...
平板电脑能玩安卓系统吗,畅享丰... 你有没有想过,平板电脑竟然也能玩安卓系统?这可不是天方夜谭,而是科技发展的新趋势。想象你手中的平板瞬...
安卓刷精简系统下载,轻松打造高... 你有没有想过,你的安卓手机是不是有点儿“臃肿”了呢?运行速度慢,电池续航短,有时候还卡得要命。别急,...
安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...