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 相关文章推荐
仅利用30行Python代码来展示X算法
Apr 01 Python
Python进阶-函数默认参数(详解)
May 18 Python
解决Spyder中图片显示太小的问题
Apr 27 Python
Python利用requests模块下载图片实例代码
Aug 12 Python
python pillow模块使用方法详解
Aug 30 Python
matplotlib绘制多个子图(subplot)的方法
Dec 03 Python
python如何通过twisted搭建socket服务
Feb 03 Python
pycharm实现在子类中添加一个父类没有的属性
Mar 12 Python
一文解决django 2.2与mysql兼容性问题
Jul 15 Python
Python实现给PDF添加水印的方法
Jan 25 Python
Python中的 Set 与 dict
Mar 13 Python
Python中的 enumerate和zip详情
May 30 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
德生S2000收音机更换“钕铁硼”全频扬声器
2021/03/02 无线电
在PHP中使用与Perl兼容的正则表达式
2006/11/26 PHP
session在PHP大型web应用中的使用
2011/06/25 PHP
ThinkPHP中自定义错误页面和提示页面实例
2014/11/22 PHP
phpStudy2016 配置多个域名期间遇到的问题小结
2017/10/19 PHP
JavaScript面象对象设计
2008/04/28 Javascript
ExtJS GTGrid 简单用户管理
2009/07/01 Javascript
使用dynatrace-ajax跟踪JavaScript的性能
2010/04/12 Javascript
js获取某月的最后一天日期的简单实例
2013/06/22 Javascript
玩转方法:call和apply
2014/05/08 Javascript
单击某一段文字改写文本颜色
2014/06/06 Javascript
我的Node.js学习之路(二)NPM模块管理
2014/07/06 Javascript
jquery配合.NET实现点击指定绑定数据并且能够一键下载
2016/10/28 Javascript
详解能在多种前端框架下使用的表格控件
2017/01/11 Javascript
JavaScript中undefined和null的区别
2017/05/03 Javascript
jquery版轮播图效果和extend扩展
2017/07/18 jQuery
vue中的scope使用详解
2017/10/29 Javascript
angular2系列之路由转场动画的示例代码
2017/11/09 Javascript
vue滚动tab跟随切换效果
2020/06/29 Javascript
Django实现快速分页的方法实例
2017/10/22 Python
win8下python3.4安装和环境配置图文教程
2018/07/31 Python
Python编程图形库之Pillow使用方法讲解
2018/12/28 Python
Pytorch 使用opnecv读入图像由HWC转为BCHW格式方式
2020/06/02 Python
python3环境搭建过程(利用Anaconda+pycharm)完整版
2020/08/19 Python
如何快速一次性卸载所有python包(第三方库)呢
2020/10/20 Python
Vans(范斯)德国官网:美国南加州的原创极限运动潮牌
2017/05/02 全球购物
中专生求职自荐信范文
2013/12/22 职场文书
清洁工表扬信
2014/01/08 职场文书
初婚未育证明
2014/01/15 职场文书
八年级语文教学反思
2014/02/11 职场文书
高三上学期学习自我评价
2014/04/23 职场文书
助理政工师申报材料
2014/06/03 职场文书
2015年保险业务员工作总结
2015/05/27 职场文书
2016大学军训心得体会
2016/01/11 职场文书
《学会生存》读后感3篇
2019/12/09 职场文书
python批量创建变量并赋值操作
2021/06/03 Python