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之不要红头文件(2)
Sep 28 Python
Python判断文本中消息重复次数的方法
Apr 27 Python
Python中max函数用于二维列表的实例
Apr 03 Python
Python2与Python3的区别实例总结
Apr 17 Python
python3用PIL把图片转换为RGB图片的实例
Jul 04 Python
python卸载后再次安装遇到的问题解决
Jul 10 Python
Django配置MySQL数据库的完整步骤
Sep 07 Python
基于Python中的yield表达式介绍
Nov 19 Python
Python for循环通过序列索引迭代过程解析
Feb 07 Python
推荐8款常用的Python GUI图形界面开发框架
Feb 23 Python
Python+PyQt5实现灭霸响指功能
May 25 Python
python 制作本地应用搜索工具
Feb 27 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
跟我学Laravel之配置Laravel
2014/10/15 PHP
教大家制作简单的php日历
2015/11/17 PHP
详解WordPress中给链接添加查询字符串的方法
2015/12/18 PHP
PHP获取路径和目录的方法总结【必看篇】
2017/03/04 PHP
超强的IE背景图片闪烁(抖动)的解决办法
2007/09/09 Javascript
js post方式传递提交的实现代码
2010/05/31 Javascript
实现图片预加载的三大方法及优缺点分析
2014/11/19 Javascript
JQuery调用绑定click事件的3种写法
2015/03/28 Javascript
js判断登陆用户名及密码是否为空的简单实例
2016/05/16 Javascript
nodejs中模块定义实例详解
2017/03/18 NodeJs
史上最全JavaScript数组去重的十种方法(推荐)
2017/08/17 Javascript
jQuery Ajax向服务端传递数组参数值的实例代码
2017/09/03 jQuery
浅谈Angular2 模块懒加载的方法
2017/10/04 Javascript
JavaScript框架Angular和React深度对比
2017/11/20 Javascript
vue 自定义提示框(Toast)组件的实现代码
2018/08/17 Javascript
JavaScript原型链与继承操作实例总结
2018/08/24 Javascript
Vue实现远程获取路由与页面刷新导致404错误的解决
2019/01/31 Javascript
使用Angular自定义字段校验指令的方法示例
2019/02/01 Javascript
vue2.0+vue-router构建一个简单的列表页的示例代码
2019/02/13 Javascript
浅谈实现在线预览PDF的几种解决办法
2020/08/10 Javascript
python获取当前运行函数名称的方法实例代码
2017/04/06 Python
Python找出微信上删除你好友的人脚本写法
2018/11/01 Python
Python图像处理之颜色的定义与使用分析
2019/01/03 Python
Python3 读取Word文件方式
2020/02/13 Python
CSS3 rgb and rgba(透明色)的使用详解
2020/09/25 HTML / CSS
阿迪达斯墨西哥官方网站:adidas墨西哥
2017/11/03 全球购物
美国球迷装备的第一来源:FOCO
2020/07/03 全球购物
小米官方旗舰店:Xiaomi
2020/08/07 全球购物
教师师德师风个人整改方案
2014/09/18 职场文书
第二批党的群众路线教育实践活动个人对照检查材料
2014/09/23 职场文书
领导干部四风问题自我剖析材料
2014/09/25 职场文书
工地材料员岗位职责
2015/04/11 职场文书
给男朋友的道歉短信
2015/05/12 职场文书
2015年高三毕业班班主任工作总结
2015/10/22 职场文书
口袋妖怪冰系十大最强精灵,几何雪花排第七,第六类似北极熊
2022/03/18 日漫
Meta增速拉垮,元宇宙难当重任
2022/04/29 数码科技