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调用java的Webservice示例
Mar 10 Python
Python 通配符删除文件的实例
Apr 24 Python
python得到windows自启动列表的方法
Oct 14 Python
django小技巧之html模板中调用对象属性或对象的方法
Nov 30 Python
python实现五子棋小游戏
Mar 25 Python
使用python绘制二元函数图像的实例
Feb 12 Python
Python3.6中Twisted模块安装的问题与解决
Apr 15 Python
Python一行代码实现快速排序的方法
Apr 30 Python
TensorFlow实现简单的CNN的方法
Jul 18 Python
python tkiner实现 一个小小的图片翻页功能的示例代码
Jun 24 Python
PyCharm 光标变成黑块的解决方式
Feb 06 Python
pytorch 中autograd.grad()函数的用法说明
May 12 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
关于mysql 字段的那个点为是定界符
2007/01/15 PHP
php抓取https的内容的代码
2010/04/06 PHP
php在项目中寻找代码的坏味道(综艺命名)
2012/07/19 PHP
PHP自动识别字符集并完成转码详解
2013/08/02 PHP
php将字符串转化成date存入数据库的两种方式
2014/04/28 PHP
Linux系统下使用XHProf和XHGui分析PHP运行性能
2015/12/08 PHP
PHP数组实例详解
2016/06/26 PHP
php基于自定义函数记录log日志方法
2017/07/21 PHP
又一个小巧的图片预加载类
2007/05/05 Javascript
js中设置元素class的三种方法小结
2011/08/28 Javascript
理解Javascript的call、apply
2015/12/16 Javascript
jQuery中slidedown与slideup方法用法示例
2016/09/16 Javascript
原生js实现水平方向无缝滚动
2017/01/10 Javascript
assert()函数用法总结(推荐)
2017/01/25 Javascript
shiro授权的实现原理
2017/09/21 Javascript
稍微学一下Vue的数据响应式(Vue2及Vue3区别)
2019/11/21 Javascript
JQuery事件冒泡和默认行为代码实例
2020/05/13 jQuery
js实现直播点击飘心效果
2020/08/19 Javascript
如何在现代JavaScript中编写异步任务
2021/01/31 Javascript
Python操作MySQL数据库的方法
2018/06/20 Python
Python Scapy随心所欲研究TCP协议栈
2018/11/20 Python
Python Multiprocessing多进程 使用tqdm显示进度条的实现
2019/08/13 Python
Python面向对象程序设计之静态方法、类方法、属性方法原理与用法分析
2020/03/23 Python
anaconda3安装及jupyter环境配置全教程
2020/08/24 Python
python利用google翻译方法实例(翻译字幕文件)
2020/09/21 Python
关于递归的一道.NET面试题
2013/05/12 面试题
计算机开发个人求职信范文
2013/09/26 职场文书
酒店应聘自荐信
2013/11/09 职场文书
小学生家长评语大全
2014/02/10 职场文书
公司门卫岗位职责
2014/03/15 职场文书
2014年教务工作总结
2014/12/03 职场文书
公路施工安全责任书
2015/05/08 职场文书
文艺节目主持词
2015/07/06 职场文书
Python基于Tkinter开发一个爬取B站直播弹幕的工具
2021/05/06 Python
Python自动操作神器PyAutoGUI的使用教程
2022/06/16 Python
详解flex:1什么意思
2022/07/23 HTML / CSS