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处理csv数据的方法
Mar 11 Python
使用 Python 实现文件递归遍历的三种方式
Jul 18 Python
基于Python实现剪切板实时监控方法解析
Sep 11 Python
在Python中利用pickle保存变量的实例
Dec 30 Python
使用Python3 poplib模块删除服务器多天前的邮件实现代码
Apr 24 Python
通过自学python能找到工作吗
Jun 21 Python
利用Python pandas对Excel进行合并的方法示例
Nov 04 Python
python 利用panda 实现列联表(交叉表)
Feb 06 Python
Python机器学习三大件之一numpy
May 10 Python
用Python编写简单的gRPC服务的详细过程
Jul 04 Python
Python学习之迭代器详解
Apr 01 Python
什么是Python装饰器?如何定义和使用?
Apr 11 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
可快速识别放射性物质-国外大神教你diy一个开放式辐射探测器
2020/03/12 无线电
PHP 第二节 数据类型之字符串类型
2012/04/28 PHP
PHP模板引擎Smarty内建函数foreach,foreachelse用法分析
2016/04/11 PHP
PHP模糊查询的实现方法(推荐)
2016/09/06 PHP
Firefox 无法获取cssRules 的解决办法
2006/10/11 Javascript
一个js的tab切换效果代码[代码分离]
2010/04/11 Javascript
仅Firefox中链接A无法实现模拟点击以触发其默认行为
2011/07/31 Javascript
jquery 查找iframe父级页面元素的实现代码
2011/08/28 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
js简单实现让文本框内容逐个字的显示出来
2013/10/22 Javascript
js如何调用qq互联api实现第三方登录
2014/03/28 Javascript
js获取input长度并根据页面宽度设置其大小及居中对齐
2014/08/22 Javascript
JS与Ajax Get和Post在使用上的区别实例详解
2016/06/08 Javascript
JS简单实现浮动窗口效果示例
2016/09/07 Javascript
强大Vue.js组件浅析
2016/09/12 Javascript
基于JS实现限时抢购倒计时间表代码
2017/05/09 Javascript
javascript实现二叉树的代码
2017/06/08 Javascript
实现div滚动条默认最底部以及默认最右边的示例代码
2017/11/15 Javascript
JS获取url参数,JS发送json格式的POST请求方法
2018/03/29 Javascript
JavaScript设计模式之观察者模式与发布订阅模式详解
2020/05/07 Javascript
node.js 如何监视文件变化
2020/09/01 Javascript
[01:19:34]2014 DOTA2国际邀请赛中国区预选赛 New Element VS Dream time
2014/05/22 DOTA
[56:18]DOTA2上海特级锦标赛主赛事日 - 4 败者组第四轮#2 MVP.Phx VS Fnatic第二局
2016/03/05 DOTA
Windows系统下使用flup搭建Nginx和Python环境的方法
2015/12/25 Python
Python 实现 贪吃蛇大作战 代码分享
2016/09/07 Python
Python端口扫描简单程序
2016/11/10 Python
Python使用getpass库读取密码的示例
2017/10/10 Python
python实现银联支付和支付宝支付接入
2019/05/07 Python
python Zmail模块简介与使用示例
2020/12/19 Python
洛杉矶生活休闲而精致的基础品牌:Mika Jaymes
2018/01/07 全球购物
如何写一个Java类既可以用作applet也可以用作java应用
2016/01/18 面试题
拾金不昧表扬稿
2015/01/16 职场文书
婚庆司仪开场白
2015/05/29 职场文书
2015暑期爱心支教策划书
2015/07/14 职场文书
如何解决php-fpm启动不了问题
2021/11/17 PHP
使用python将HTML转换为PDF pdfkit包(wkhtmltopdf) 的使用方法
2022/04/21 Python