在Python的Django框架上部署ORM库的教程


Posted in Python onApril 20, 2015

Python ORM 概览

作为一个美妙的语言,Python 除了 SQLAlchemy 外还有很多ORM库。在这篇文章里,我们将来看看几个流行的可选ORM 库,以此更好地窥探到Python ORM 境况。通过写一段脚本来读写2个表 ,person 和 address 到一个简单的数据库,我们能更好地理解每个ORM库的优缺点。

SQLObject

SQLObject 是一个介于SQL数据库和Python之间映射对象的Python ORM。得益于其类似于Ruby on Rails的ActiveRecord模式,在编程社区变得越来越流行。首个 SQLObject在2002年十月发布。它遵循LGPL许可。

在 SQLObject 中,数据库概念是通过与 SLQAlchemy 非常类似的的一种方式映射到Python的,表映射成类,行作为实例而字段作为属性。它同时提供一种基于Python对象的查询语言,这使得SQL 更加抽象, 从而为应用提供了数据库不可知性(译注:应用和数据库分离)
 

$ pip install sqlobject
Downloading/unpacking sqlobject
Downloading SQLObject-1.5.1.tar.gz (276kB): 276kB downloaded
Running setup.py egg_info for package sqlobject
 
warning: no files found matching '*.html'
warning: no files found matching '*.css'
warning: no files found matching 'docs/*.html'
warning: no files found matching '*.py' under directory 'tests'
Requirement already satisfied (use --upgrade to upgrade): FormEncode>=1.1.1 in /Users/xiaonuogantan/python2-workspace/lib/python2.7/site-packages (from sqlobject)
Installing collected packages: sqlobject
Running setup.py install for sqlobject
changing mode of build/scripts-2.7/sqlobject-admin from 644 to 755
changing mode of build/scripts-2.7/sqlobject-convertOldURI from 644 to 755
 
warning: no files found matching '*.html'
warning: no files found matching '*.css'
warning: no files found matching 'docs/*.html'
warning: no files found matching '*.py' under directory 'tests'
changing mode of /Users/xiaonuogantan/python2-workspace/bin/sqlobject-admin to 755
changing mode of /Users/xiaonuogantan/python2-workspace/bin/sqlobject-convertOldURI to 755
Successfully installed sqlobject
Cleaning up...
 
>>> from sqlobject import StringCol, SQLObject, ForeignKey, sqlhub, connectionForURI
>>> sqlhub.processConnection = connectionForURI('sqlite:/:memory:')
>>>
>>> class Person(SQLObject):
... name = StringCol()
...
>>> class Address(SQLObject):
... address = StringCol()
... person = ForeignKey('Person')
...
>>> Person.createTable()
[]
>>> Address.createTable()
[]

上面的代码创建了2个简单的表:person 和 address 。为了创建和插入记录到这2个表,我们简单实例化一个person 实例和 一个 address 实例:
 

>>> p = Person(name='person')
>>> a = Address(address='address', person=p)
>>> p
 
>>> a
 
<address>

为了获得或检索新记录, 我们用神奇的 q 对象关联到 Person 和 Address 类:

 

>>> persons = Person.select(Person.q.name == 'person')
>>> persons
 
>>> list(persons)
[]
>>> p1 = persons[0]
>>> p1 == p
True
>>> addresses = Address.select(Address.q.person == p1)
>>> addresses
 
>>> list(addresses)
[
 
<address>]
>>> a1 = addresses[0]
>>> a1 == a
True
Storm

Storm 是一个介于 单个或多个数据库与Python之间 映射对象的 Python ORM 。为了支持动态存储和取回对象信息,它允许开发者构建跨数据表的复杂查询。它由Ubuntu背后的公司 Canonical公司用Python开发的,用在 Launchpad 和 Landscape 应用中,后来在2007年作为自由软件发布。这个项目在LGPL许可下发布,代码贡献者必须受让版权给Canonical公司。

像 SQLAlchemy 和 SQLObject 那样, Storm 也映射表到类,行到实例和字段到属性。相对另外2个库, Stom中 table class 不需要是框架特定基类 的子类 。在 SQLAlchemy中,每个 table class 是 sqlalchemy.ext.declarative.declarative_bas 的一个子类。 而在SQLOjbect中,每个table class是 的 sqlobject.SQLObject 的子类。
 

类似于 SQLAlchemy, Storm 的 Store 对象对于后端数据库就像一个代理人, 所有的操作缓存在内存,一当提交方法在store上被调用就提交到数据库。每个 store 持有自己的Python数据库对象映射集合,就像一个 SQLAlchemy session 持有不同的 Python对象集合。

指定版本的 Storm 可以从 下载页面 下载。在这篇文章里,示例代码是使用 0.20 版本的Storm写的。
 

>>> from storm.locals import Int, Reference, Unicode, create_database, Store
>>>
>>>
>>> db = create_database('sqlite:')
>>> store = Store(db)
>>>
>>>
>>> class Person(object):
... __storm_table__ = 'person'
... id = Int(primary=True)
... name = Unicode()
...
>>>
>>> class Address(object):
... __storm_table__ = 'address'
... id = Int(primary=True)
... address = Unicode()
... person_id = Int()
... person = Reference(person_id, Person.id)
...

上面的代码创建了一个 sqlite 内存数据库,然后用 store 来引用该数据库对象。一个Storm store 类似 SQLAlchemy的 DBSession对象,都管理 附属于其的实例对象 的生命周期。例如,下面的代码创建了一个 person 和 一个 address, 然后通过刷新 store 都插入记录。
 

>>> store.execute("CREATE TABLE person "
... "(id INTEGER PRIMARY KEY, name VARCHAR)")
 
>>> store.execute("CREATE TABLE address "
... "(id INTEGER PRIMARY KEY, address VARCHAR, person_id INTEGER, "
... " FOREIGN KEY(person_id) REFERENCES person(id))")
 
>>> person = Person()
>>> person.name = u'person'
>>> print person
 
>>> print "%r, %r" % (person.id, person.name)
None, u'person' # Notice that person.id is None since the Person instance is not attached to a valid database store yet.
>>> store.add(person)
 
>>> print "%r, %r" % (person.id, person.name)
None, u'person' # Since the store hasn't flushed the Person instance into the sqlite database yet, person.id is still None.
>>> store.flush()
>>> print "%r, %r" % (person.id, person.name)
1, u'person' # Now the store has flushed the Person instance, we got an id value for person.
>>> address = Address()
>>> address.person = person
>>> address.address = 'address'
>>> print "%r, %r, %r" % (address.id, address.person, address.address)
None, , 'address'
>>> address.person == person
True
>>> store.add(address)
 
>>> store.flush()
>>> print "%r, %r, %r" % (address.id, address.person, address.address)
1, , 'address'

为了获得或检索已插的 Person 和 Address 对象, 我们调用 store.find() 来查询:

 

>>> person = store.find(Person, Person.name == u'person').one()
>>> print "%r, %r" % (person.id, person.name)
1, u'person'
>>> store.find(Address, Address.person == person).one()
 
>>> address = store.find(Address, Address.person == person).one()
>>> print "%r, %r" % (address.id, address.address)
1, u'address'

Django 的 ORM

Django 是一个免费开源的紧嵌ORM到其系统的web应用框架。在它首次发布后,得益于其易用为Web而备的特点,Django越来越流行。它在2005年七月在BSD许可下发布。因为Django的ORM 是紧嵌到web框架的,所以就算可以也不推荐,在一个独立的非Django的Python项目中使用它的ORM。

Django,一个最流行的Python web框架, 有它独有的 ORM。 相比 SQLAlchemy, Django 的 ORM 更吻合于直接操作SQL对象,操作暴露了简单直接映射数据表和Python类的SQL对象 。

 

$ django-admin.py startproject demo
$ cd demo
$ python manage.py syncdb
Creating tables ...
Creating table django_admin_log
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
 
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): no
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
$ python manage.py shell

因为我们在没有先建立一个项目时不能够执行Django代码,所以我们在前面的shell创建一个Django demo 项目,然后进入Django shell来测试我们写的 ORM 例子。

 

# demo/models.py
>>> from django.db import models
>>>
>>>
>>> class Person(models.Model):
... name = models.TextField()
... class Meta:
... app_label = 'demo'
...
>>>
>>> class Address(models.Model):
... address = models.TextField()
... person = models.ForeignKey(Person)
... class Meta:
... app_label = 'demo'
...

上面的代码声明了2个Python 类,Person 和 Address,每一个都映射到数据库表。在执行任意数据库操作代码之前,我们需要先在本地的sqlite数据库创建表。

python manage.py syncdb
Creating tables ...
Creating table demo_person
Creating table demo_address
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)

为了插入一个 person 和一个 address 到数据库,我们实例化相应对象并调用这些对象的save() 方法。
 

>>> from demo.models import Person, Address
>>> p = Person(name='person')
>>> p.save()
>>> print "%r, %r" % (p.id, p.name)
1, 'person'
>>> a = Address(person=p, address='address')
>>> a.save()
>>> print "%r, %r" % (a.id, a.address)
1, 'address'

为了获得或检索 person 和 address 对象, 我们用model类神奇的对象属性从数据库取得对象。
 

>>> persons = Person.objects.filter(name='person')
>>> persons
[]
>>> p = persons[0]
>>> print "%r, %r" % (p.id, p.name)
1, u'person'
>>> addresses = Address.objects.filter(person=p)
>>> addresses
[
 
<address>]
>>> a = addresses[0]
>>> print "%r, %r" % (a.id, a.address)
1, u'address'

peewee

peewee 是一个小的,表达式的 ORM。相比其他的 ORM,peewee 主要专注于极简主义,其API简单,并且其库容易使用和理解。
 

pip install peewee
Downloading/unpacking peewee
Downloading peewee-2.1.7.tar.gz (1.1MB): 1.1MB downloaded
Running setup.py egg_info for package peewee
 
Installing collected packages: peewee
Running setup.py install for peewee
changing mode of build/scripts-2.7/pwiz.py from 644 to 755
 
changing mode of /Users/xiaonuogantan/python2-workspace/bin/pwiz.py to 755
Successfully installed peewee
Cleaning up...

为了创建数据库模型映射,我们实现了一个Person 类 和一个Address类 来映射对应的数据库表。

 

>>> from peewee import SqliteDatabase, CharField, ForeignKeyField, Model
>>>
>>> db = SqliteDatabase(':memory:')
>>>
>>> class Person(Model):
... name = CharField()
... class Meta:
... database = db
...
>>>
>>> class Address(Model):
... address = CharField()
... person = ForeignKeyField(Person)
... class Meta:
... database = db
...
>>> Person.create_table()
>>> Address.create_table()

为了插入对象到数据库,我们实例化对象并调用了它们的save() 方法。从视图的对象创建这点来看,peewee类似于Django。
 

>>> p = Person(name='person')
>>> p.save()
>>> a = Address(address='address', person=p)
>>> a.save()

为了从数据库获得或检索对象, 我们select 了类各自的对象。

 

>>> person = Person.select().where(Person.name == 'person').get()
>>> person
 
>>> print '%r, %r' % (person.id, person.name)
1, u'person'
>>> address = Address.select().where(Address.person == person).get()
>>> print '%r, %r' % (address.id, address.address)
1, u'address'

SQLAlchemy

 SQLAlchemy 是Python编程语言里,一个在MIT许可下发布的开源工具和SQL ORM。它首次发布于2006年二月,由Michael Bayer写的。它提供了 “一个知名企业级的持久化模式的,专为高效率和高性能的数据库访问设计的,改编成一个简单的Python域语言的完整套件”。它采用了数据映射模式(像Java中的Hibernate)而不是Active Record模式(像Ruby on Rails的ORM)。

SQLAlchemy 的工作单元 主要使得 有必要限制所有的数据库操作代码到一个特定的数据库session,在该session中控制每个对象的生命周期 。类似于其他的ORM,我们开始于定义declarative_base()的子类,以映射表到Python类。

 

>>> from sqlalchemy import Column, String, Integer, ForeignKey
>>> from sqlalchemy.orm import relationship
>>> from sqlalchemy.ext.declarative import declarative_base
>>>
>>>
>>> Base = declarative_base()
>>>
>>>
>>> class Person(Base):
... __tablename__ = 'person'
... id = Column(Integer, primary_key=True)
... name = Column(String)
...
>>>
>>> class Address(Base):
... __tablename__ = 'address'
... id = Column(Integer, primary_key=True)
... address = Column(String)
... person_id = Column(Integer, ForeignKey(Person.id))
... person = relationship(Person)
...

在我们写任何数据库代码前,我们需要为数据库session创建一个数据库引擎。
 

>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///')

一当我们创建了数据库引擎,可以继续创建一个数据库会话,并为所有之前定义的 Person和Address 类创建数据库表。
 

>>> from sqlalchemy.orm import sessionmaker
>>> session = sessionmaker()
>>> session.configure(bind=engine)
>>> Base.metadata.create_all(engine)

现在,session 对象对象变成了我们工作单元的构造函数,将和所有后续数据库操作代码和对象关联到一个通过调用它的 __init__() 方法构建的数据库session上。
 

>>> s = session()
>>> p = Person(name='person')
>>> s.add(p)
>>> a = Address(address='address', person=p)
>>> s.add(a)

为了获得或检索数据库中的对象,我们在数据库session对象上调用 query() 和 filter() 方法。
 

>>> p = s.query(Person).filter(Person.name == 'person').one()
>>> p
 
>>> print "%r, %r" % (p.id, p.name)
1, 'person'
>>> a = s.query(Address).filter(Address.person == p).one()
>>> print "%r, %r" % (a.id, a.address)
1, 'address'

请留意到目前为止,我们还没有提交任何对数据库的更改,所以新的person和address对象实际上还没存储在数据库中。 调用 s.commit() 将会提交更改,比如,插入一个新的person和一个新的address到数据库中。
 

>>> s.commit()
>>> s.close()

Python ORM 之间对比

对于在文章里提到的每一种 Python ORM ,我们来列一下他们的优缺点:
SQLObject

优点:

  •     采用了易懂的ActiveRecord 模式
  •     一个相对较小的代码库

缺点:

  •     方法和类的命名遵循了Java 的小驼峰风格
  •     不支持数据库session隔离工作单元

Storm

优点:

  •     清爽轻量的API,短学习曲线和长期可维护性
  •     不需要特殊的类构造函数,也没有必要的基类

缺点:

  •     迫使程序员手工写表格创建的DDL语句,而不是从模型类自动派生
  •     Storm的贡献者必须把他们的贡献的版权给Canonical公司

Django's ORM

优点:

  •     易用,学习曲线短
  •     和Django紧密集合,用Django时使用约定俗成的方法去操作数据库

缺点:

  •     不好处理复杂的查询,强制开发者回到原生SQL
  •     紧密和Django集成,使得在Django环境外很难使用

peewee

优点:

  •     Django式的API,使其易用
  •     轻量实现,很容易和任意web框架集成

缺点:

  •     不支持自动化 schema 迁移
  •     多对多查询写起来不直观

SQLAlchemy

优点:

  •     企业级 API,使得代码有健壮性和适应性
  •     灵活的设计,使得能轻松写复杂查询

缺点:

  •     工作单元概念不常见
  •     重量级 API,导致长学习曲线

总结和提示

相比其他的ORM, SQLAlchemy 意味着,无论你何时写SQLAlchemy代码, 都专注于工作单元的前沿概念 。DB Session 的概念可能最初很难理解和正确使用,但是后来你会欣赏这额外的复杂性,这让意外的时序提交相关的数据库bug减少到0。在SQLAlchemy中处理多数据库是棘手的, 因为每个DB session 都限定了一个数据库连接。但是,这种类型的限制实际上是好事, 因为这样强制你绞尽脑汁去想在多个数据库之间的交互, 从而使得数据库交互代码很容易调试。

在未来的文章中,我们将会完整地披露更高阶的SQLAlchemy用例, 真正领会无限强大的API。

Python 相关文章推荐
win10下Python3.6安装、配置以及pip安装包教程
Oct 01 Python
Python cookbook(数据结构与算法)字典相关计算问题示例
Feb 18 Python
Python爬虫设置代理IP的方法(爬虫技巧)
Mar 04 Python
Windows下Python3.6安装第三方模块的方法
Nov 22 Python
python虚拟环境完美部署教程
Aug 06 Python
Python使用requests xpath 并开启多线程爬取西刺代理ip实例
Mar 06 Python
python 写一个性能测试工具(一)
Oct 24 Python
python Zmail模块简介与使用示例
Dec 19 Python
利用Python过滤相似文本的简单方法示例
Feb 03 Python
python自动化办公操作PPT的实现
Feb 05 Python
conda安装tensorflow和conda常用命令小结
Feb 20 Python
如何理解python接口自动化之logging日志模块
Jun 15 Python
在Heroku云平台上部署Python的Django框架的教程
Apr 20 #Python
从Python程序中访问Java类的简单示例
Apr 20 #Python
把项目从Python2.x移植到Python3.x的经验总结
Apr 20 #Python
python使用7z解压apk包的方法
Apr 18 #Python
python使用装饰器和线程限制函数执行时间的方法
Apr 18 #Python
python使用multiprocessing模块实现带回调函数的异步调用方法
Apr 18 #Python
python对指定目录下文件进行批量重命名的方法
Apr 18 #Python
You might like
咖啡知识 除了喝咖啡还有那些知识点
2021/03/06 新手入门
smarty基础之拼接字符串的详解
2013/06/18 PHP
PHP判断文章里是否有图片的简单方法
2014/07/26 PHP
PHP动态规划解决0-1背包问题实例分析
2015/03/23 PHP
PHP递归实现汉诺塔问题的方法示例
2017/11/25 PHP
php利用array_search与array_column实现二维数组查找
2019/07/08 PHP
JSON 编辑器实现代码
2009/12/06 Javascript
jquery特效 幻灯片效果示例代码
2013/07/16 Javascript
js点击事件链接的问题解决
2014/04/25 Javascript
jQuery限制图片大小的方法
2016/05/25 Javascript
原生JS实现在线问卷调查投票特效
2017/01/03 Javascript
angular.fromJson与toJson方法用法示例
2017/05/17 Javascript
微信小程序后台解密用户数据实例详解
2017/06/28 Javascript
jquery一键控制checkbox全选、反选或全不选
2017/10/16 jQuery
解决Vue使用mint-ui loadmore实现上拉加载与下拉刷新出现一个页面使用多个上拉加载后冲突问题
2017/11/07 Javascript
jQuery实现的页面弹幕效果【测试可用】
2018/08/17 jQuery
JavaScript事件发布/订阅模式原理与用法分析
2018/08/21 Javascript
vue进入页面时滚动条始终在底部代码实例
2019/03/26 Javascript
JS前端知识点 运算符优先级,URL编码与解码,String,Math,arguments操作整理总结
2019/06/27 Javascript
微信小程序学习总结(三)条件、模板、文件引用实例分析
2020/06/04 Javascript
Python中函数参数设置及使用的学习笔记
2016/05/03 Python
git进行版本控制心得详谈
2017/12/10 Python
Python实现读取字符串按列分配后按行输出示例
2018/04/17 Python
django 修改server端口号的方法
2018/05/14 Python
Pytorch 搭建分类回归神经网络并用GPU进行加速的例子
2020/01/09 Python
使用keras和tensorflow保存为可部署的pb格式
2020/05/25 Python
如何解决flask修改静态资源后缓存文件不能及时更改问题
2020/08/02 Python
Python基于Socket实现简易多人聊天室的示例代码
2020/11/29 Python
css3 边框、背景、文本效果的实现代码
2018/03/21 HTML / CSS
bareMinerals官网:矿物质化妆品和护肤品
2018/02/04 全球购物
美国正宗奢华复古手袋、珠宝及配饰网站:What Goes Around Comes Around
2018/07/21 全球购物
YII2 全局异常处理深入讲解
2021/03/24 PHP
公司出纳岗位职责
2013/12/07 职场文书
中学劳技课教师的自我评价
2014/02/05 职场文书
品牌推广策划方案
2014/05/28 职场文书
教师学习心得体会范文
2016/01/21 职场文书