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继承和抽象类的实现方法
Jan 14 Python
在Python中使用next()方法操作文件的教程
May 24 Python
python UNIX_TIMESTAMP时间处理方法分析
Apr 18 Python
浅析使用Python操作文件
Jul 31 Python
Python之Scrapy爬虫框架安装及使用详解
Nov 16 Python
基于numpy.random.randn()与rand()的区别详解
Apr 17 Python
基于Python列表解析(列表推导式)
Jun 23 Python
python设定并获取socket超时时间的方法
Jan 12 Python
Pycharm 实现下一个文件引用另外一个文件的方法
Jan 17 Python
matplotlib画混淆矩阵与正确率曲线的实例代码
Jun 01 Python
Anaconda安装pytorch和paddle的方法步骤
Apr 03 Python
使用Django框架创建项目
Jun 10 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创建session的方法实例详解
2015/01/27 PHP
如何利用http协议发布博客园博文评论
2015/08/03 PHP
详解WordPress中的头像缓存和代理中的缓存更新方法
2016/03/01 PHP
PHP中文字符串截断无乱码解决方法
2016/10/10 PHP
php获取开始与结束日期之间所有日期的方法
2016/11/29 PHP
JavaScript实现禁止后退的方法
2006/12/27 Javascript
Javascript学习笔记1 数据类型
2010/01/11 Javascript
基于JQuery的访问WebService的代码(可访问Java[Xfire])
2010/11/19 Javascript
jQuery控制TR显示隐藏的几种方法
2014/06/18 Javascript
jQuery.parseJSON(json)将JSON字符串转换成js对象
2014/07/27 Javascript
jQuery图片特效插件Revealing实现拉伸放大
2015/04/22 Javascript
JavaScript中this的9种应用场景及三种复合应用场景
2015/09/12 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
微信小程序之获取当前位置经纬度以及地图显示详解
2017/05/09 Javascript
老生常谈Bootstrap媒体对象
2017/07/06 Javascript
bootstrap multiselect下拉列表功能
2017/08/22 Javascript
解决vue更新路由router-view复用组件内容不刷新的问题
2019/11/04 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
2020/04/11 Javascript
利用soaplib搭建webservice详细步骤和实例代码
2013/11/20 Python
python使用psutil模块获取系统状态
2016/08/27 Python
详解python中requirements.txt的一切
2017/03/03 Python
Flask 让jsonify返回的json串支持中文显示的方法
2018/03/26 Python
python对离散变量的one-hot编码方法
2018/07/11 Python
Python3 无重复字符的最长子串的实现
2019/10/08 Python
Python List列表对象内置方法实例详解
2019/10/22 Python
使用python和pygame制作挡板弹球游戏
2019/12/03 Python
详解Python GUI编程之PyQt5入门到实战
2020/12/10 Python
使用gunicorn部署django项目的问题
2020/12/30 Python
NULL是什么,它是怎么定义的
2015/05/09 面试题
大学生毕业求职简历的自我评价
2013/10/24 职场文书
大学毕业生自荐书怎么写?
2014/01/06 职场文书
区域销售主管岗位职责
2014/06/15 职场文书
英文版辞职信
2015/02/28 职场文书
大学生党课心得体会
2016/01/07 职场文书
教你快速开启Apache SkyWalking的自监控
2021/04/25 Servers
java后台调用接口及处理跨域问题的解决
2022/03/24 Java/Android