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通过字典dict判断指定键值是否存在的方法
Mar 21 Python
以一个投票程序的实例来讲解Python的Django框架使用
Feb 18 Python
Python的mysql数据库的更新如何实现
Jul 31 Python
Python中xrange与yield的用法实例分析
Dec 26 Python
分析python动态规划的递归、非递归实现
Mar 04 Python
python用户管理系统
Mar 13 Python
django缓存配置的几种方法详解
Jul 16 Python
pyqt5实现绘制ui,列表窗口,滚动窗口显示图片的方法
Jun 20 Python
pandas进行时间数据的转换和计算时间差并提取年月日
Jul 06 Python
tensorflow:指定gpu 限制使用量百分比,设置最小使用量的实现
Feb 06 Python
python实现udp传输图片功能
Mar 20 Python
详解Python中第三方库Faker
Sep 25 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
ThinkPHP3.1新特性之对Ajax的支持更加完善
2014/06/19 PHP
护卫神php套件 php版本升级方法(php5.5.24)
2015/05/10 PHP
php常量详细解析
2015/10/27 PHP
详解WordPress中创建和添加过滤器的相关PHP函数
2015/12/29 PHP
laravel自定义分页效果
2017/07/23 PHP
Div Select挡住的解决办法
2008/08/07 Javascript
JavaScript 异步调用框架 (Part 2 - 用例设计)
2009/08/03 Javascript
Javascript中判断变量是数组还是对象(array还是object)
2013/08/14 Javascript
用JS将搜索的关键字高亮显示实现代码
2013/11/08 Javascript
JQuery each打印JS对象的方法
2013/11/13 Javascript
JS实现双击编辑可修改状态的方法
2015/08/14 Javascript
学习JavaScript图片预加载模块
2016/11/07 Javascript
微信小程序之ES6与事项助手的功能实现
2016/11/30 Javascript
jquery对象与DOM对象转化
2017/02/08 Javascript
深入探究angular2 UI组件之primeNG用法
2017/07/26 Javascript
vue实现裁切图片同时实现放大、缩小、旋转功能
2018/03/02 Javascript
利用js实现前后台传送Json的示例代码
2018/03/29 Javascript
浅谈Angular HttpClient简单入门
2018/05/04 Javascript
bootstrap table表格插件之服务器端分页实例代码
2018/09/12 Javascript
浅谈在不使用ssr的情况下解决Vue单页面SEO问题(2)
2018/11/08 Javascript
深入浅析vue-cli@3.0 使用及配置说明
2019/05/08 Javascript
JavaScript中如何对多维数组(矩阵)去重的实现
2019/12/04 Javascript
Python 流程控制实例代码
2009/09/25 Python
Python基于递归算法实现的走迷宫问题
2017/08/04 Python
windows下cx_Freeze生成Python可执行程序的详细步骤
2018/10/09 Python
python 计算一个字符串中所有数字的和实例
2019/06/11 Python
Python CVXOPT模块安装及使用解析
2019/08/01 Python
Python使用matplotlib绘制Logistic曲线操作示例
2019/11/28 Python
机械设计职业生涯规划书
2013/12/27 职场文书
初中教师业务学习材料
2014/05/12 职场文书
525心理活动总结
2014/07/04 职场文书
教育系统干部作风整顿心得体会
2014/09/09 职场文书
大学生入党自我鉴定范文
2019/06/21 职场文书
Python实现查询剪贴板自动匹配信息的思路详解
2021/07/09 Python
vue如何实现关闭对话框后刷新列表
2022/04/08 Vue.js
win11怎么消除图标小盾牌?win11消除图标小盾牌解决方法
2022/08/05 数码科技