Python中编写ORM框架的入门指引

作者:廖雪峰 时间:2021-06-21 04:20:59 

有了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
0
投稿

猜你喜欢

  • python输入一个水仙花数(三位数) 输出百位十位个位实例

    2022-12-09 12:39:58
  • Python如何查看并打印matplotlib中所有的colormap(cmap)类型

    2023-12-05 11:04:28
  • Django应用程序中如何发送电子邮件详解

    2023-11-04 02:07:54
  • Python 中的单分派泛函数你真的了解吗

    2023-03-12 04:32:26
  • 提高JavaScript执行效率的23个实用技巧

    2023-08-15 18:38:12
  • python 获取网页编码方式实现代码

    2023-07-21 08:15:14
  • Jupyter notebook在mac:linux上的配置和远程访问的方法

    2023-06-20 06:11:01
  • 我跟iframe之间的误会

    2008-03-17 13:30:00
  • php源码的使用方法讲解

    2023-06-17 21:47:02
  • 使用Python脚本来获取Cisco设备信息的示例

    2023-05-26 22:27:20
  • 详解如何在 Linux 中安装最新的 Python 3.6 版本

    2022-03-25 15:06:21
  • python中的不可变数据类型与可变数据类型详解

    2022-12-27 21:56:24
  • python生成图片验证码的方法

    2022-07-27 13:39:11
  • LINUX下Oracle数据库用户创建方法详解

    2023-07-22 02:02:20
  • 八卦调侃Reset CSS

    2010-01-13 13:01:00
  • Python真题案例之二分法查找详解

    2023-09-23 01:39:07
  • python脚本打包后无法运行exe文件的解决方案

    2021-02-25 23:25:35
  • Web开发者的百科全书——Google DocType

    2008-07-03 13:06:00
  • 使用Python抓取模板之家的CSS模板

    2022-04-09 18:35:52
  • 初探MS SQL CE+Codesmith

    2009-05-11 09:03:00
  • asp之家 网络编程 m.aspxhome.com