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网络编程学习笔记(八):XML生成与解析(DOM、ElementTree)
Jun 09 Python
Django的数据模型访问多对多键值的方法
Jul 21 Python
深入理解python函数递归和生成器
Jun 06 Python
win10系统中安装scrapy-1.1
Jul 03 Python
python模块简介之有序字典(OrderedDict)
Dec 01 Python
在Python中使用AOP实现Redis缓存示例
Jul 11 Python
python Web开发你要理解的WSGI & uwsgi详解
Aug 01 Python
Python离线安装PIL 模块的方法
Jan 08 Python
Python利用pandas处理Excel数据的应用详解
Jun 18 Python
python并发编程 Process对象的其他属性方法join方法详解
Aug 20 Python
浅谈Python基础之列表那些事儿
May 11 Python
Python 避免字典和元组的多重嵌套问题
Jul 15 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防范SQL注入的具体方法详解(测试通过)
2014/05/09 PHP
PHP获取服务器端信息的方法
2014/11/28 PHP
PHP动态输出JavaScript代码实例
2015/02/12 PHP
ThinkPHP中where()使用方法详解
2016/04/19 PHP
php中__toString()方法用法示例
2016/12/07 PHP
thinkPHP引入类的方法详解
2016/12/08 PHP
javascript 一些用法小结
2009/09/11 Javascript
JS判断页面加载状态以及添加遮罩和缓冲动画的代码
2012/10/11 Javascript
jquery中交替点击事件的实现代码
2014/02/14 Javascript
php,js,css字符串截取的办法集锦
2014/09/26 Javascript
jQuery的图片滑块焦点图插件整理推荐
2014/12/07 Javascript
Shell脚本实现Linux系统和进程资源监控
2015/03/05 Javascript
jQuery实现默认是闭合的FAQ展开效果菜单
2015/09/14 Javascript
js遮罩效果制作弹出注册界面效果
2017/01/25 Javascript
利用JS对iframe父子(内外)页面进行操作的方法教程
2017/06/15 Javascript
JavaScript变量作用域_动力节点Java学院整理
2017/06/27 Javascript
vue-cli3使用 DllPlugin 实现预编译提升构建速度
2019/04/24 Javascript
vue 动态给每个页面添加title、关键词和描述的方法
2020/08/28 Javascript
动态实现element ui的el-table某列数据不同样式的示例
2021/01/22 Javascript
python实现dnspod自动更新dns解析的方法
2014/02/14 Python
一个基于flask的web应用诞生 记录用户账户登录状态(6)
2017/04/11 Python
Python数据结构之栈、队列的实现代码分享
2017/12/04 Python
使用python制作一个解压缩软件
2019/11/13 Python
python利用proxybroker构建爬虫免费IP代理池的实现
2021/02/21 Python
美国领先的在线邮轮旅游公司:CruiseDirect
2018/06/07 全球购物
印尼极简主义和实惠的在线家具店:Fabelio
2019/03/27 全球购物
如何使用PHP session
2015/04/21 面试题
两年的个人工作自我评价
2014/01/10 职场文书
总经理助理职责
2014/02/04 职场文书
查摆问题自我剖析材料
2014/08/18 职场文书
意外伤害赔偿协议书
2014/09/16 职场文书
群众路线教育实践活动对照检查材料思想汇报(副处级领导)
2014/10/04 职场文书
寝室长工作失责检讨书
2014/10/06 职场文书
新郎新娘答谢词
2015/01/04 职场文书
实验心得体会范文
2016/01/25 职场文书
win10系统计算机图标怎么调出来?win10调出计算机图标的方法
2022/08/14 数码科技