使用Python的web.py框架实现类似Django的ORM查询的教程


Posted in Python onMay 02, 2015

Django中的对象查询

Django框架自带了ORM,实现了一些比较强大而且方便的查询功能,这些功能和表无关。比如下面这个例子:

class Question(models.Model):
  question_text = models.CharField(max_length=200)
  pub_date = models.DateTimeField('date published')


>>> Question.objects.all()
>>> Question.objects.get(pk=1)

从例子可以看出,objects.all和objects.get这些功能都不是在class Question中定义的,可能在其父类models.Model中定义,也可能不是。那么我们在web.py中如何实现这样的功能呢?(如果你选择使用SQLAlchemy就不需要自己实现了)。
实现
思路

我们注意到Question.objects.all()这样的调用是直接访问了类属性objects,并调用了objects属性的方法all()。这里objects可能是一个实例,也可能是一个类。我个人认为(我没看过Django的实现)这应该是一个实例,因为实例化的过程可以传递一些表的信息,使得类似all()这样的函数可以工作。经过分析之后,我们可以列出我们需要解决的问题:

  •     需要实现一个模型的父类Model,实际的表可以从这个父类继承以获得自己没有定义的功能。
  •     实际的模型类(比如Question类)定义后,不实例话的情况下就要具备objects.all()这样的查询效果。
  • 从上面的需求可以看出,我们需要在类定义的时候就实现这些功能,而不是等到类实例化的时候再实现这些功能。类定义的时候实现功能?这不就是metaclass(元类)做的事情嘛。因此实现过程大概是下面这样的:
  •     实现一个Model类,其绑定方法和表的增、删、改有关。
  •     修改Model类的元类为ModelMetaClass,该元类定义的过程中为类增加一个objects对象,该对象是一个ModelDefaultManager类的实例,实现了表的查询功能。

代码

都说不给代码就是耍流氓,我还是给吧。说明下:使用的数据库操作都是web.py的db库中的接口。

# -*- coding: utf-8 -*-

  import web

  import config # 自定义的配置类,可以忽略


  def _connect_to_db():
    return web.database(dbn="sqlite", db=config.dbname)


  def init_db():
    db = _connect_to_db()
    for statement in config.sql_statements:
      db.query(statement)


  class ModelError(Exception):
    """Exception raised by all models.

    Attributes:
      msg: Error message.
    """

    def __init__(self, msg=""):
      self.msg = msg

    def __str__(self):
      return "ModelError: %s" % self.msg


  class ModelDefaultManager(object):
    """ModelManager implements query functions against a model.

    Attributes:
      cls: The class to be managed.
    """

    def __init__(self, cls):
      self.cls = cls
      self._table_name = cls.__name__.lower()

    def all(self):
      db = _connect_to_db()
      results = db.select(self._table_name)
      return [self.cls(x) for x in results]

    def get(self, query_vars, where):
      results = self.filter(query_vars, where, limit=1)
      if len(results) > 0:
        return results[0]
      else:
        return None

    def filter(self, query_vars, where, limit=None):
      db = _connect_to_db()
      try:
        results = db.select(self._table_name, vars=query_vars, where=where,
                  limit=limit)
      except (Exception) as e:
        raise ModelError(str(e))

      return [self.cls(x) for x in results]


  class ModelMetaClass(type):

    def __new__(cls, classname, bases, attrs):
      new_class = super(ModelMetaClass, cls).__new__(cls, classname,
                              bases, attrs)
      objects = ModelDefaultManager(new_class)
      setattr(new_class, "objects", objects)

      return new_class


  class Model(object):
    """Parent class of all models.
    """

    __metaclass__ = ModelMetaClass

    def __init__(self):
      pass

    def _table_name(self):
      return self.__class__.__name__.lower()

    def insert(self, **kargs):
      db = _connect_to_db()
      try:
        with db.transaction():
          db.insert(self._table_name(), **kargs)
      except (Exception) as e:
        raise ModelError(str(e))

    def delete(self, where, using=None, vars=None):
      db = _connect_to_db()
      try:
        with db.transaction():
          db.delete(self._table_name(), where, vars=vars)
      except (Exception) as e:
        raise ModelError(str(e))

    def save(self, where, vars=None, **kargs):
      db = _connect_to_db()
      try:
        with db.transaction():
          db.update(self._table_name(), where, vars, **kargs)
      except (Exception) as e:
        raise ModelError(str(e))

使用

首先定义表对应的类:

class Users(Model):
  ...

使用就和Django的方式一样:

>>> user_list = Users.objects.all()

 

Python 相关文章推荐
详解Python中的变量及其命名和打印
Mar 11 Python
python中字符串的操作方法大全
Jun 03 Python
python实现雨滴下落到地面效果
Jun 21 Python
python leetcode 字符串相乘实例详解
Sep 03 Python
Flask框架web开发之零基础入门
Dec 10 Python
分享Python切分字符串的一个不错方法
Dec 14 Python
python 自定义对象的打印方法
Jan 12 Python
解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available
Jul 31 Python
pycharm修改file type方式
Nov 19 Python
解决Python pip 自动更新升级失败的问题
Feb 21 Python
python collections模块的使用
Oct 16 Python
python在package下继续嵌套一个package
Apr 14 Python
在ironpython中利用装饰器执行SQL操作的例子
May 02 #Python
用Python编写简单的定时器的方法
May 02 #Python
用Python程序抓取网页的HTML信息的一个小实例
May 02 #Python
在Mac OS上部署Nginx和FastCGI以及Flask框架的教程
May 02 #Python
在Python的Django框架中用流响应生成CSV文件的教程
May 02 #Python
详细解读Python中的__init__()方法
May 02 #Python
举例讲解Python的Tornado框架实现数据可视化的教程
May 02 #Python
You might like
PHP,ASP.JAVA,JAVA代码格式化工具整理
2010/06/15 PHP
php中preg_replace正则替换用法分析【一次替换多个值】
2017/01/17 PHP
微信公众号开发之获取位置信息php代码
2018/06/13 PHP
PHP使Laravel为JSON REST API返回自定义错误的问题
2018/10/16 PHP
extjs 学习笔记(三) 最基本的grid
2009/10/15 Javascript
一个可以兼容IE FF的加为首页与加入收藏实现代码
2009/11/02 Javascript
JavaScript表单通过正则表达式验证电话号码
2014/03/14 Javascript
javascript将浮点数转换成整数的三个方法
2014/06/23 Javascript
通用javascript代码判断版本号是否在版本范围之间
2015/11/29 Javascript
微信小程序 参数传递详解
2016/10/24 Javascript
[js高手之路]设计模式系列课程-发布者,订阅者重构购物车的实例
2017/08/29 Javascript
JavaScript惰性求值的一种实现方法示例
2019/01/11 Javascript
从零到一详聊创建Vue工程及遇到的常见问题
2019/04/25 Javascript
纯js实现无缝滚动功能代码实例
2020/02/21 Javascript
vue实现简单全选和反选功能
2020/09/15 Javascript
js用正则表达式筛选年月日的实例方法
2021/01/04 Javascript
JavaScript 绘制饼图的示例
2021/02/19 Javascript
[01:04]DOTA2:伟大的Roshan雕塑震撼来临
2015/01/30 DOTA
python模拟登陆Tom邮箱示例分享
2014/01/13 Python
利用python库在局域网内传输文件的方法
2018/06/04 Python
面向初学者的Python编辑器Mu
2018/10/08 Python
jupyter notebook 中输出pyecharts图实例
2020/04/23 Python
使用Django实现把两个模型类的数据聚合在一起
2020/03/28 Python
Python类绑定方法及非绑定方法实例解析
2020/10/09 Python
新东网科技Java笔试题
2012/07/13 面试题
校园十大歌手策划书
2014/02/01 职场文书
安全检查管理制度
2014/02/02 职场文书
作文评语集锦大全
2014/04/23 职场文书
2014年中秋寄语
2014/08/11 职场文书
群众路线剖析材料(四风)
2014/11/05 职场文书
环保守法证明
2015/06/24 职场文书
师德培训心得体会2016
2016/01/09 职场文书
心得体会该怎么写呢?
2019/06/27 职场文书
Go语言切片前或中间插入项与内置copy()函数详解
2021/04/27 Golang
javascript函数式编程基础
2021/09/15 Javascript
js中Object.create实例用法详解
2021/10/05 Javascript