Python中编写ORM框架的入门指引


Posted in Python onApril 29, 2015

有了db模块,操作数据库直接写SQL就很方便。但是,我们还缺少ORM。如果有了ORM,就可以用类似这样的语句获取User对象:

user = User.get('123')

而不是写SQL然后再转换成User对象:

u = db.select_one('select * from users where id=?', '123')
user = User(**u)

所以我们开始编写ORM模块:transwarp.orm。
设计ORM接口

和设计db模块类似,设计ORM也是从上层调用者角度来设计。

我们先考虑如何定义一个User对象,然后把数据库表users和它关联起来。

from transwarp.orm import Model, StringField, IntegerField

class User(Model):
  __table__ = 'users'
  id = IntegerField(primary_key=True)
  name = StringField()

注意到定义在User类中的__table__、id和name是类的属性,不是实例的属性。所以,在类级别上定义的属性用来描述User对象和表的映射关系,而实例属性必须通过__init__()方法去初始化,所以两者互不干扰:

# 创建实例:
user = User(id=123, name='Michael')
# 存入数据库:
user.insert()

实现ORM模块

有了定义,我们就可以开始实现ORM模块。

首先要定义的是所有ORM映射的基类Model:

class Model(dict):
  __metaclass__ = ModelMetaclass

  def __init__(self, **kw):
    super(Model, self).__init__(**kw)

  def __getattr__(self, key):
    try:
      return self[key]
    except KeyError:
      raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

  def __setattr__(self, key, value):
    self[key] = value

Model从dict继承,所以具备所有dict的功能,同时又实现了特殊方法__getattr__()和__setattr__(),所以又可以像引用普通字段那样写:

>>> user['id']
123
>>> user.id
123

Model只是一个基类,如何将具体的子类如User的映射信息读取出来呢?答案就是通过metaclass:ModelMetaclass:

class ModelMetaclass(type):
  def __new__(cls, name, bases, attrs):
    mapping = ... # 读取cls的Field字段
    primary_key = ... # 查找primary_key字段
    __table__ = cls.__talbe__ # 读取cls的__table__字段
    # 给cls增加一些字段:
    attrs['__mapping__'] = mapping
    attrs['__primary_key__'] = __primary_key__
    attrs['__table__'] = __table__
    return type.__new__(cls, name, bases, attrs)

这样,任何继承自Model的类(比如User),会自动通过ModelMetaclass扫描映射关系,并存储到自身的class中。

然后,我们往Model类添加class方法,就可以让所有子类调用class方法:

class Model(dict):

  ...

  @classmethod
  def get(cls, pk):
    d = db.select_one('select * from %s where %s=?' % (cls.__table__, cls.__primary_key__.name), pk)
    return cls(**d) if d else None

User类就可以通过类方法实现主键查找:

user = User.get('123')

往Model类添加实例方法,就可以让所有子类调用实例方法:

class Model(dict):

  ...

  def insert(self):
    params = {}
    for k, v in self.__mappings__.iteritems():
      params[v.name] = getattr(self, k)
    db.insert(self.__table__, **params)
    return self

这样,就可以把一个User实例存入数据库:

user = User(id=123, name='Michael')
user.insert()

最后一步是完善ORM,对于查找,我们可以实现以下方法:

find_first()

  find_all()

  find_by()

对于count,可以实现:

  

count_all()

 count_by()

以及update()和delete()方法。

最后看看我们实现的ORM模块一共多少行代码?加上注释和doctest才仅仅300多行。用Python写一个ORM是不是很容易呢?

Python 相关文章推荐
Python动态加载模块的3种方法
Nov 22 Python
一些常用的Python爬虫技巧汇总
Sep 28 Python
centos 安装python3.6环境并配置虚拟环境的详细教程
Feb 22 Python
django的登录注册系统的示例代码
May 14 Python
Python图像处理之颜色的定义与使用分析
Jan 03 Python
Flask模板引擎之Jinja2语法介绍
Jun 26 Python
python opencv 读取图片 返回图片某像素点的b,g,r值的实现方法
Jul 03 Python
python实现爬取百度图片的方法示例
Jul 06 Python
python切片的步进、添加、连接简单操作示例
Jul 11 Python
TensorFlow实现批量归一化操作的示例
Apr 22 Python
基于python实现操作redis及消息队列
Aug 27 Python
Python办公自动化之教你用Python批量识别发票并录入到Excel表格中
Jun 26 Python
python获取本机mac地址和ip地址的方法
Apr 29 #Python
在Python中编写数据库模块的教程
Apr 29 #Python
Python的gevent框架的入门教程
Apr 29 #Python
在Python中使用HTML模版的教程
Apr 29 #Python
以Flask为例讲解Python的框架的使用方法
Apr 29 #Python
详解Python程序与服务器连接的WSGI接口
Apr 29 #Python
Python的SQLAlchemy框架使用入门
Apr 29 #Python
You might like
php_xmlhttp 乱码问题解决方法
2009/08/07 PHP
探讨:如何使用PHP实现计算两个日期间隔的年、月、周、日数
2013/06/13 PHP
PHP开发中常见的安全问题详解和解决方法(如Sql注入、CSRF、Xss、CC等)
2014/04/21 PHP
JS setCapture 区域外事件捕捉
2010/03/18 Javascript
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
2013/04/26 Javascript
JS+CSS实现简单滑动门(滑动菜单)效果
2015/09/19 Javascript
javascript实现uploadify上传格式以及个数限制
2015/11/23 Javascript
JS加载iFrame出现空白问题的解决办法
2016/05/13 Javascript
使用Curl命令查看请求响应时间方法
2016/11/04 Javascript
简单理解Vue条件渲染
2016/12/03 Javascript
JS实现微信弹出搜索框 多条件查询功能
2016/12/13 Javascript
微信小程序 五星评价功能的实现
2017/03/09 Javascript
微信小程序封装http访问网络库实例代码
2017/05/24 Javascript
js学习总结之DOM2兼容处理顺序问题的解决方法
2017/07/27 Javascript
深入理解 webpack 文件打包机制(小结)
2018/01/08 Javascript
vue.js2.0点击获取自己的属性和jquery方法
2018/02/23 jQuery
利用vscode调试编译后的js代码详解
2018/05/14 Javascript
Vux+Axios拦截器增加loading的问题及实现方法
2018/11/08 Javascript
js实现ATM机存取款功能
2020/10/27 Javascript
[01:04:01]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第一场
2014/05/24 DOTA
python爬虫入门教程之点点美女图片爬虫代码分享
2014/09/02 Python
Python 2.7.x 和 3.x 版本的重要区别小结
2014/11/28 Python
Python使用openpyxl读写excel文件的方法
2017/06/30 Python
利用python的socket发送http(s)请求方法示例
2018/05/07 Python
Python使用folium excel绘制point
2019/01/03 Python
python创建ArcGIS shape文件的实现
2019/12/06 Python
PyTorch学习:动态图和静态图的例子
2020/01/06 Python
基于opencv的selenium滑动验证码的实现
2020/07/24 Python
CSS3实现各种图形的示例代码
2016/10/19 HTML / CSS
Python中如何定义一个函数
2016/09/06 面试题
英语专业个人求职自荐信
2013/09/21 职场文书
高中军训感想800字
2014/02/23 职场文书
火锅店营销方案
2014/02/26 职场文书
检讨书大全
2015/01/27 职场文书
暑期实践个人总结
2015/03/06 职场文书
Python开发五子棋小游戏
2022/05/02 Python