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线程锁(thread)学习示例
Dec 04 Python
python实时分析日志的一个小脚本分享
May 07 Python
wxPython的安装图文教程(Windows)
Dec 28 Python
python人民币小写转大写辅助工具
Jun 20 Python
Python实现矩阵相乘的三种方法小结
Jul 26 Python
PyCharm代码回滚,恢复历史版本的解决方法
Oct 22 Python
celery4+django2定时任务的实现代码
Dec 23 Python
python爬虫把url链接编码成gbk2312格式过程解析
Jun 08 Python
详解Python 中的容器 collections
Aug 17 Python
Python 使用Opencv实现目标检测与识别的示例代码
Sep 08 Python
python绘制趋势图的示例
Sep 17 Python
python自动化八大定位元素讲解
Jul 09 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
对Session和Cookie的区分与解释
2007/03/16 PHP
Mysql数据库操作类( 1127版,提供源码下载 )
2010/12/02 PHP
phpword插件导出word文件时中文乱码问题处理方案
2014/08/19 PHP
Yii框架form表单用法实例
2014/12/04 PHP
php启用sphinx全文搜索的实现方法
2014/12/24 PHP
使用PHP生成图片的缩略图的方法
2015/08/18 PHP
PHP SFTP实现上传下载功能
2017/07/26 PHP
JSONP 跨域访问代理API-yahooapis实现代码
2012/12/02 Javascript
js导航菜单(自写)简单大方
2013/03/28 Javascript
深入理解JavaScript系列(47):对象创建模式(上篇)
2015/03/04 Javascript
简介JavaScript中的push()方法的使用
2015/06/09 Javascript
详解vue+css3做交互特效的方法
2017/11/20 Javascript
详解如何将 Vue-cli 改造成支持多页面的 history 模式
2017/11/20 Javascript
Promise.all中对于reject的处理方法
2018/08/01 Javascript
vue 使用自定义指令实现表单校验的方法
2018/08/28 Javascript
浅析微信小程序modal弹窗关闭默认会执行cancel问题
2019/10/14 Javascript
KnockoutJS数组比较算法实例详解
2019/11/25 Javascript
JS如何定义用字符串拼接的变量
2020/07/11 Javascript
Vue中component标签解决项目组件化操作
2020/09/04 Javascript
python使用win32com在百度空间插入html元素示例
2014/02/20 Python
Python编程实现线性回归和批量梯度下降法代码实例
2018/01/04 Python
对Python3中bytes和HexStr之间的转换详解
2018/12/04 Python
实例讲解Python中浮点型的基本内容
2019/02/11 Python
如何实现在jupyter notebook中播放视频(不停地展示图片)
2020/04/23 Python
使用openCV去除文字中乱入的线条实例
2020/06/02 Python
Pycharm自带Git实现版本管理的方法步骤
2020/09/18 Python
多视角3D可旋转的HTML5 Logo动画
2016/03/02 HTML / CSS
个人自我鉴定范文
2013/10/04 职场文书
门诊挂号室室长岗位职责
2013/11/27 职场文书
美德好少年主要事迹
2014/01/29 职场文书
党员公开承诺书范文
2014/03/25 职场文书
2014年法制宣传日活动方案
2014/11/02 职场文书
安全教育片观后感
2015/06/17 职场文书
2016年国庆节假期旅游工作总结
2016/04/01 职场文书
JavaScript实现酷炫的鼠标拖尾特效
2022/02/18 Javascript
详解Python中*args和**kwargs的使用
2022/04/07 Python