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 相关文章推荐
Python2.7简单连接与操作MySQL的方法
Apr 27 Python
简单讲解Python编程中namedtuple类的用法
Jun 21 Python
python 二分查找和快速排序实例详解
Oct 13 Python
TensorFlow实现简单卷积神经网络
May 24 Python
Scrapy基于selenium结合爬取淘宝的实例讲解
Jun 13 Python
django进阶之cookie和session的使用示例
Aug 17 Python
python3中eval函数用法使用简介
Aug 02 Python
python多线程案例之多任务copy文件完整实例
Oct 29 Python
解决pyqt5异常退出无提示信息的问题
Apr 08 Python
TensorFlow使用Graph的基本操作的实现
Apr 22 Python
Python图片检索之以图搜图
May 31 Python
python实现A*寻路算法
Jun 13 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
php array_walk() 数组函数
2011/07/12 PHP
jQuery插件 tabBox实现代码
2010/02/09 Javascript
jquery 操作日期、星期、元素的追加的实现代码
2012/02/07 Javascript
Jquery使用Firefox FireBug插件调试Ajax步骤讲解
2013/12/02 Javascript
Javascript实现的常用算法(如冒泡、快速、鸽巢、奇偶等)
2014/04/29 Javascript
javascript实现图像循环明暗变化的方法
2015/02/25 Javascript
基于 Node.js 实现前后端分离
2016/04/23 Javascript
使用Promise解决多层异步调用的简单学习心得
2016/05/17 Javascript
JS判断iframe是否加载完成的方法
2016/08/03 Javascript
整理一些最近经常遇到的前端面试题
2017/04/25 Javascript
React Native中TabBarIOS的简单使用方法示例
2017/10/13 Javascript
详解Chai.js断言库API中文文档
2018/01/31 Javascript
Vue使用vue-area-linkage实现地址三级联动效果的示例
2018/06/27 Javascript
JavaScript计算正方形面积
2019/11/26 Javascript
js中addEventListener()与removeEventListener()用法案例分析
2020/03/02 Javascript
vue实现全屏滚动效果(非fullpage.js)
2020/03/07 Javascript
多种类型jQuery网页验证码插件代码实例
2021/01/09 jQuery
[49:21]2018DOTA2亚洲邀请赛3月30日 小组赛B组 Effect VS iG
2018/03/31 DOTA
[02:38]2018年度DOTA2最佳劣单位选手-完美盛典
2018/12/17 DOTA
Python实现单词拼写检查
2015/04/25 Python
Python处理XML格式数据的方法详解
2017/03/21 Python
Django Admin 实现外键过滤的方法
2017/09/29 Python
python实现指定ip端口扫描方式
2019/12/17 Python
Super-Pharm波兰:药房和香水在一个地方
2020/08/18 全球购物
Java面试题:Java类的Main方法如果是Private将会怎么样
2016/08/18 面试题
竞聘上岗演讲稿范文
2014/01/10 职场文书
司机检讨书
2014/02/13 职场文书
学校消防演习方案
2014/02/19 职场文书
捐献物资倡议书范文
2014/05/19 职场文书
暑期培训心得体会
2014/09/02 职场文书
公安机关纪律作风整顿剖析
2014/10/10 职场文书
2015元旦主持词开场白和结束语
2014/12/14 职场文书
酒店辞职信怎么写
2015/02/27 职场文书
升学宴学生致辞
2015/07/27 职场文书
解决Swagger2返回map复杂结构不能解析的问题
2021/07/02 Java/Android
解决redis批量删除key值的问题
2022/03/23 Redis