概述

什么是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 = name
self.column_type = column_type
def __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] = v
for 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 = setattr
if 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):
pass def 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 ==> <IntegerField:uid>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Found class: Person
Found mapping: pid ==> <IntegerField:id>
Found mapping: names ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField: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 = name
self.column_type = column_type def __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] = v
for 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 = setattr
if 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):
pass def delete(self):
pass 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()

Python 元类编程实现一个简单的 ORM的更多相关文章

  1. python3 元类编程的一个例子

    [引子] 虽然我们可以通过“class”语句来定义“类”,但是要想更加细粒度的控制“类”的创建,要使用元类编程才能实现. 比如说我们要实现这样的一个约束.所有项目中用到的类都应该要为它定义的方法提供文 ...

  2. Python元类编程

    来源:http://python.jobbole.com/88582/ @property装饰器,是将类中的函数当做属性调用 Python类中定义的属性,如果属性名前面只有一个下划线,那么就是一种规范 ...

  3. 3.python元类编程

     1.1.propety动态属性 在面向对象编程中,我们一般把名词性的东西映射成属性,动词性的东西映射成方法.在python中他们对应的分别是属性self.xxx和类方法.但有时我们需要的属性需要根据 ...

  4. PythonI/O进阶学习笔记_7.python动态属性,__new__和__init__和元类编程(上)

    content: 上: 1.property动态属性 2.__getattr__和__setattr__的区别和在属性查找中的作用 3.属性描述符 和属性查找过程 4.__new__和__init__ ...

  5. python的元类编程

    廖雪峰的python教程有python元类编程示例,综合代码如下 https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df ...

  6. Python进阶开发之元类编程

    系列文章 √第一章 元类编程,已完成 ; 本文目录 类是如何产生的如何使用type创建类理解什么是元类使用元类的意义元类实战:ORM . 类是如何产生的 类是如何产生?这个问题肯定很傻.实则不然,很多 ...

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

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

  8. Python元类实践--自己定义一个和collections中一样的namedtuple

    大家可能很熟悉在collections模块中有一个很好用的扩展数据类型-namedtuple. 如果你还不知道这个类型,那么请翻看标准手册. 我利用元类轻松定义一个namedtuple. 先把代码贴上 ...

  9. python+selenium之自定义封装一个简单的Log类

    python+selenium之自定义封装一个简单的Log类 一. 问题分析: 我们需要封装一个简单的日志类,主要有以下内容: 1. 生成的日志文件格式是 年月日时分秒.log 2. 生成的xxx.l ...

随机推荐

  1. HTTP笔记1--Web及网络基础

    web页面如何呈现? 客户端:通过发送请求获取服务器资源的 Web 浏览器 web是建立在 HTTP 协议上通信的   WWW(万维网/web)的构建技术 把 SGML(StandardGeneral ...

  2. forEachRemaining()方法的用法

    forEachRemaining()是java1.8新增的Iterator接口中的默认方法对于这个方法,官方文档是这么描述的:Performs the given action for each re ...

  3. [整理] LRU 算法的实现方式

    目录 概念 方法选择 实现方案(基于LinkedHashMap) 改进方案 1.LRU-K 2.Two queue 3.Multi Queue(MQ) LRU类算法对比 LRU 在 Redis 中的应 ...

  4. python之字符串方法upper/lower

    1.描述: upper():用于将字符串全部转换为大写字母 lower():用于将字符串全部转换为小写字母 2.语法 str.upper() str.lower() 3.返回值 upper()或low ...

  5. Redis面试常见问题(一)

    一.redis 简介简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向.另外,redis 也经 ...

  6. 痞子衡嵌入式:超级下载算法(RT-UFL)开发笔记(3) - 统一FlexSPI驱动访问

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是超级下载算法开发笔记(3)之统一FlexSPI驱动访问. 文接上篇 <超级下载算法(RT-UFL)开发笔记(2) - 识别当前i. ...

  7. value-key

    value-key object 如果 Select 的绑定值为对象类型,请务必指定 value-key 作为它的唯一性标识. value-key 作为 value 唯一标识的键名,绑定值为对象类型时 ...

  8. 阅文集团 招聘官网 bug

    阅文集团 招聘官网 bug https://join.yuewen.com/ 前端开发 zxx.jpg 张鑫旭 https://qidian.gtimg.com/yuewen/join/css/ima ...

  9. Node.js Learning Paths

    Node.js Learning Paths Node.js in Action Node.js Expert situations / scenario Restful API OAuth 2.0 ...

  10. Flutter Vignettes

    Flutter Vignettes Flutter Animation https://flutter.gskinner.com/ https://github.com/gskinnerTeam/fl ...