ORM概念

ORM(Object Ralational Mapping,对象关系映射)用来把对象模型表示的对象映射到基于 SQL  的关系模型数据库结构中去。这样,我们在具体的操作实体对象的时候,就不需要再去和复杂的 SQL 语句打交道,只需简单的操作实体对象的属性和方法。

一个句话理解就是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句。

示例:

class User(父类省略):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
...省略... u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()
# 对应如下sql语句
# insert into User (username,email,password,uid)
# values ('Michael','test@orm.org','my-pwd',12345)

__new__、__init__、__call__的介绍

在讲使用元类创建ORM之前,必须了解__new__这个内置方法的作用。

__new__方法负责创建一个实例对象,在对象被创建的时候调用该方法它是一个类方法。__new__方法在返回一个实例之后,会自动的调用__init__方法,对实例进行初始化。如果__new__方法不返回值,或者返回的不是实例,那么它就不会自动的去调用__init__方法。

__init__ 方法负责将该实例对象进行初始化,在对象被创建之后调用该方法,在__new__方法创建出一个实例后对实例属性进行初始化。__init__方法可以没有返回值。

__call__方法其实和类的创建过程和实例化没有多大关系了,定义了__call__方法才能被以函数的方式执行。

class A(object):
def __call__(self):
print "__call__ be called" a = A()
a() # 输出:__call__ be called

通过元类简单实现ORM中的insert功能

class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判断是否需要保存
for k, v in attrs.items():
# 判断是否是指定的StringField或者IntegerField的实例对象
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v # 删除这些已经在字典中存储的属性
for k in mappings.keys():
attrs.pop(k) # 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs) class User(metaclass=ModelMetaclass):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
# 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储
# 以上User类中有
# __mappings__ = {
# "uid": ('uid', "int unsigned")
# "name": ('username', "varchar(30)")
# "email": ('email', "varchar(30)")
# "password": ('password', "varchar(30)")
# }
# __table__ = "User"
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value) # 设置属性值 def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None)) args_temp = list()
for temp in args:
# 判断入如果是数字类型
if isinstance(temp, int):
args_temp.append(str(temp))
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp) # 处理字符串类型的数据
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql) u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()

输出结果

Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

抽取到基类中

class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判断是否需要保存
for k, v in attrs.items():
# 判断是否是指定的StringField或者IntegerField的实例对象
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v # 将提取的内容存放在一个字典中 # 删除这些已经在字典中存储的属性
for k in mappings.keys():
attrs.pop(k) # 将之前的uid/name/email/password以及对应的对象引用、类名字
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
attrs['__table__'] = name # 假设表名和类名一致
return type.__new__(cls, name, bases, attrs) class Model(object, metaclass=ModelMetaclass):
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value) # 将name,value设置为实例属性 def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
# fields.append(v[0]) # v为类属性的元组
fields.append(k) # 如果使用v[0],那就是取元组中的uid、username等为数据库表中字段名,不利于理解,直接取类属性的变量名
args.append(getattr(self, k)) # k为类属性的变量名 args_temp = list()
for temp in args:
# 判断入如果是数字类型
if isinstance(temp, int):
args_temp.append(str(temp))
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql) class User(Model): # User对应数据库中的表名
# uid = ('uid', "int unsigned")
# username = ('name', "varchar(30)")
# email = ('email', "varchar(30)")
# password = ('password', "varchar(30)")
uid = ("int unsigned",) # 数据库字操作的字段以这里的字段名为主,实例化传的变量名,主要用来匹配各个字段对应的值,可以多但不能少,否则报错
username = ("varchar(30)",)
email = ("varchar(30)",)
password = ("varchar(30)",) u = User(uid=12345, username='Michael', email='test@orm.org', password='my-pwd', phone=12345678900)
# print(u.__dict__)
u.save()

输出结果

Found mapping: uid ==> ('int unsigned',)
Found mapping: username ==> ('varchar(30)',)
Found mapping: email ==> ('varchar(30)',)
Found mapping: password ==> ('varchar(30)',)
SQL: insert into User (uid,username,email,password) values (12345,'Michael','test@orm.org','my-pwd')

通过上面的示例,我们可以看出用元类创建API是非常好的选择,使用元类的编写虽然很复杂,但使用者可以非常简洁的调用API。

Python 元类实现ORM的更多相关文章

  1. python 元类理解

    原文来自:https://segmentfault.com/a/1190000011447445 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生万物 我是谁?我从哪来里?我要到哪里去? ...

  2. python元类:type和metaclass

    python元类:type和metaclass python中一切皆对象,所以类本身也是对象.类有创建对象的能力,那谁来创建类的呢?答案是type. 1.用tpye函数创建一个类 class A(ob ...

  3. Python 元类 - Metaclasses

    Python 元类 - Metaclasses 默认情况下儿, classes 是有 type() 构造的. 类的结构体在一个新的 namespace 被执行, 类的名字 class name 绑定( ...

  4. Python进阶丨如何创建你的第一个Python元类?

    摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...

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

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第19篇文章,我们一起来用元类实现一个简易的ORM数据库框架. 本文主要是受到了廖雪峰老师Python3入门教程的启 ...

  6. python 元类

    转载自  http://blog.jobbole.com/21351/ 类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大 ...

  7. 元类实现ORM

    1. ORM是什么 ORM 是 python编程语言后端web框架 Django的核心思想,"Object Relational Mapping",即对象-关系映射,简称ORM. ...

  8. 使用元类 编写ORM

    元类 一句话: 元类定制类的创建行为 知识点 1.类的创建: python这种动态语言,函数和类的定义,不是编译时定义的,而是运行时动态创建的. Python解释器遇到class定义时,仅仅是扫描一下 ...

  9. Python元类(metaclass)以及元类实现单例模式

    这里将一篇写的非常好的文章基本照搬过来吧,这是一篇在Stack overflow上很热的帖子,我看http://blog.jobbole.com/21351/这篇博客对其进行了翻译. 一.理解类也是对 ...

随机推荐

  1. logrotate没有rotate的排查过程

    前言 背景 xxx,你过来把squid的日志检查一下,是否做了日志切割:于是乎开启了logrotate没有切割日志的排查旅程,em--.只能说过程很爽,平时疲于应付繁琐的事情,难得有点时间能一条线慢慢 ...

  2. 05 - Vue3 UI Framework - Button 组件

    官网基本做好了,接下来开始做核心组件 返回阅读列表点击 这里 目录准备 在项目 src 目录下创建 lib 文件夹,用来存放所有的核心组件吧.然后再在 lib 文件夹下创建 Button.vue 文件 ...

  3. 关于导入Eclips Web项目报错的解决方案

    1.是一定要有耐心,耐心,耐心,重要的事情说三遍.针对问题一 一破解,一步一步来,不要放弃. 2.其实百度就好了他们有报错的各种问题及解决方案 ,包括导入项目web.xml报错,js文件,jsp文件报 ...

  4. mysqlslap基准测试

    目录 简介 二.例子 三.其它选项 简介 mysqlslap是mysql自带的基准测试工具 优点:查询数据,语法简单,灵活容易使用.该工具可以模拟多个客户端同时并发的向服务器发出查询更新,给出了性能测 ...

  5. Python绘制饼图

    Python绘制饼图 1.1 对应代码如下图所示 import matplotlib.pyplot as pltfrom pylab import mplmpl.rcParams['font.sans ...

  6. vue文件上传及压缩(canvas实现压缩)

    // 读取文件结果 afterRead(files) { let that = this; let file = files.file; if (file === undefined) { retur ...

  7. 【js基础】基础数据类型变量为啥有属性?

    1.变量和数值 let a =1 这是一个简单的变量声明,其中"a"是变量,在代码中供程序员或者语法操作的,而1是数值,是我最终需要的东西.为什么不直接使用数值而使用变量?这个就不 ...

  8. centos7 ssh 提示/bin/bash No such file or directory 【ldd命令理解】

    现象:客户报障ssh无法登陆.提示/bin/bash No such file or directory 排查:进入单用户模式 linux16 行ro替换 rw init=/sysroot/bin/s ...

  9. JuiceFS 数据加密原理

    JuiceFS 作为分布文件系统,每天与海量的数据打着交道,因此数据的安全性尤为关键,今天就来介绍一下 JuiceFS 在数据加密方面所做的努力. 传输中数据加密 JuiceFS 在网络上传输时会对数 ...

  10. Coder 投稿 | mPaaS 的多版本接入(Android)

    本文作者:mPaaS 用户「Q-Coder」 同时欢迎更多的开发者向 mPaaS 投稿 原文:blog.csdn.net/yqq577/article/details/116801705 前言 对于 ...