昨天晚上找了找关于 Djongo中 实现的 ORM的博客,其中有一篇讲的不错,但是仍然有很多地方对于我这个刚学 Python不久的新人来说理解起来很吃力的东西。
现在呢,我有一份简化版的源码,当然,也是自己根据理解一个字一个字敲上去的,这里面呢,我用注释的方式大体把 ORM 的实现过程描述了一遍,也算是Python学习中的一个提高。
这里呢,有两个问题需要解决:
【1】第一个问题: ORM 到底是什么?
【2】第二个问题:我要用python中的元类实现 ORM ,我需要怎样一个大体的思路?
【1】什么是 ORM ?
ORM 就是python编程语言 后端web框架 Django的核心思想,叫做“对象—关系映射”,简称 ORM。看到一篇大神的博客上是这样解释的,相对理解容易点 —— ORM思想就是,你创建一个实例对象,用创建它的 类名当作表名,用 创建它的 类属性当作数据表的字段名,当对这个实例对象操作的时候,就相当于是对 Mysql语句的操作。其实呢,说的就是简化了开发者的操作复杂度。
【2】我要用python中的元类实现 ORM ,我需要怎样一个大体的思路?
说实话,对于我这样一个刚接触 python 不久的小白来说,确实有难度,也看了很多 CSDN上大佬的博客,确实不好理解,逻辑性很强,还好最后坚持下来,串了一遍,勉强理解了 ORM 大体的工作流程。这里呢,描述起来确实不简单,对于网络上流传的那些高难度的版本,我确实俺不懂,由于期间很多知识的缺失,我也就只能达到这个水平,我是理解不了那种炒鸡复杂的东西了。但是,它的大体思路我是弄明白了,下面呢,由于逻辑性比较强,所以呢,这里根据流传出来的一个简化版的ORM 实现流程进行讲解,我把思路用注释的形式写到源码上了。
**下面是图片版源码及执行结果,方便查看**
下面是执行结果:
# 定义一个类,它继承 元类
class ModeMetaclass(type):
# 重写 __new__方法,这里为什么重写__new__方法而不是别的,因为此时__new__方法可以接收所有需要的得到的信息
def __new__(cls, name, base, attrs):
# cls——>"代表了类对象" name——>"User" base——>"()空元组" attrs——>"{"uid":(...), "name":(...), "email":(....),....}"
mapping = dict()
# 判断是否需要保存
for k, v in attrs.items():
# 对 attrs 这个字典拆包 key=k, value=v
# print("------------K-----------:",k)
# print("------------V-----------:",v)
if isinstance(v, tuple):
# 如果判断 attrs 的值为”元组“类型,就把 k当作键,v当作值放到字典 mapping中
mapping[k] = v
for i in mapping.keys():
# 删除attrs字典中与mapping字典中相同的元素
attrs.pop(i)
# 把 mapping字典保存到 attrs字典中
attrs["__mapping__"] = mapping
# 把 User 这个类名保存到 attrs这个字典中去
attrs["__table__"] = name
# 返回type这个父类原来没有重写的__new__方法,因为咱自己重写的__new__方法不能创建实例,所以需要借助父类的__new__方法来创建
return type.__new__(cls, name, base, attrs)
'''
上面这个类,以我自己的看法,它完成两件事:
第一件事,就是把下面的类 User的类属性 【即——> uid=(...)/name=(...)/email=(...)/password=(...)】
这几个类属性,转换成 __mapping__ = {'uid':('uid',"int udesigned"),'name':('username',"varchar(30)",......)} 这个类属性。
并且增加了 __table__这个类属性。因此,此时下面这个 User 类,实际上现在只有 __mapping__ 和 __table__这两个类属性。
第二件事,就是调用父类的 __new__方法,完成实例的创建,只不过,此时传进父类(即 type)中的参数,是已经修改过的参数。
'''
class User(metaclass=ModeMetaclass):
uid = ('uid', "int udesigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
def __init__(self, **kwargs):
# 这里 __init__方法中 **kwargs 会把传进来的值以字典的形式整理出来
# 接下来,我们需要拆包这个字典,把 键 和值分离出来,给这个实例对象添他特有的属性
for name, value in kwargs.items():
# 给实例添加实例属性【name = value】
setattr(self, name, value)
def save(self):
fields = []
args = []
# 把 __mapping__这个字典中的元素拆包
for k, v in self.__mapping__.items():
# print("----------2K--------------",k)
# print("----------2V--------------",v)
# 把 __mapping__字典的值(它的值是个元组)里面的索引值为0的取出来添加到fields这个列表中
fields.append(v[0])
# 把__mapping__字典中的值所对应的这个实例属性中的属性值加入到args列表中
args.append(getattr(self, k, None))
# print("------getattr---------",getattr(self, k, None))
print("===============fields===========", fields)
print("===============args=============", args)
# sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
args_temp = list()
for temp in args:
# 如果 args 中的某个元素是整数类型,就把它变成字符串类型
if isinstance(temp, int):
args_temp.append(str(temp))
# 如果 args 中某个元素是字符串类型,就把它两边再加上一层双引号
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQ: %s ' % sql)
def main():
# 我们这里用 数据库里差入数据作为研究对象来研究Djongo中的 ORM 是怎么实现的?
user = User(uid=123456, name="Caiden_Micheal", email="[email protected]", password=123456789)
user.save()
if __name__ == '__main__':
main()