以 我的理解,对于Python中的元类实现Django的核心思想ORM的原理剖析

以 我的理解,对于Python中的元类实现ORM的原理剖析

昨天晚上找了找关于 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 实现流程进行讲解,我把思路用注释的形式写到源码上了。
**下面是图片版源码及执行结果,方便查看**以 我的理解,对于Python中的元类实现Django的核心思想ORM的原理剖析_第1张图片下面是执行结果:以 我的理解,对于Python中的元类实现Django的核心思想ORM的原理剖析_第2张图片

下面是源码,方便复制,可以自己练练手尝试尝试:
# 定义一个类,它继承 元类
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()

你可能感兴趣的:(笔记)