在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 相关文章推荐
从零学python系列之浅谈pickle模块封装和拆封数据对象的方法
May 23 Python
Python爬虫利用cookie实现模拟登陆实例详解
Jan 12 Python
Python编程之微信推送模板消息功能示例
Aug 21 Python
使用Py2Exe for Python3创建自己的exe程序示例
Oct 31 Python
python3实现小球转动抽奖小游戏
Apr 15 Python
基于树莓派的语音对话机器人
Jun 17 Python
python科学计算之narray对象用法
Nov 25 Python
PyCharm如何导入python项目的方法
Feb 06 Python
Flask模板引擎Jinja2使用实例
Apr 23 Python
python输出数学符号实例
May 11 Python
python右对齐的实例方法
Jul 05 Python
python更新数据库中某个字段的数据(方法详解)
Nov 18 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
使用swoole扩展php websocket示例
2014/02/13 PHP
php算法实例分享
2015/07/14 PHP
PHP 5.6.11 访问SQL Server2008R2的几种情况详解
2016/08/08 PHP
php插入含有特殊符号数据的处理方法
2016/11/24 PHP
php+mysql实现简单登录注册修改密码网页
2016/11/30 PHP
php-fpm重启导致的程序执行中断问题详解
2019/04/29 PHP
javascript面向对象编程代码
2011/12/19 Javascript
利用javascript实现全部删或清空所选的操作
2014/05/27 Javascript
node.js+Ajax实现获取HTTP服务器返回数据
2014/11/26 Javascript
整理Javascript基础语法学习笔记
2015/11/29 Javascript
Jquery 1.9.1源码分析系列(十二)之筛选操作
2015/12/02 Javascript
js如何改变文章的字体大小
2016/01/08 Javascript
Javascript数组Array方法解读
2016/03/13 Javascript
深入理解jquery中的事件与动画
2016/05/24 Javascript
JavaScript调试之console.log调试的一个小技巧分享
2017/08/07 Javascript
swiper移动端轮播插件(触碰图片之后停止轮播)
2017/12/28 Javascript
.vue文件 加scoped 样式不起作用的解决方法
2018/05/28 Javascript
详解基于electron制作一个node压缩图片的桌面应用
2019/01/29 Javascript
详解小程序毫秒级倒计时(适用于拼团秒杀功能)
2019/05/05 Javascript
[43:41]VP vs RNG 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.21.mp4
2020/07/19 DOTA
[36:33]完美世界DOTA2联赛PWL S2 LBZS vs Forest 第二场 11.29
2020/12/02 DOTA
python使用正则表达式检测密码强度源码分享
2014/06/11 Python
深入解析Python中的集合类型操作符
2015/08/19 Python
利用Pandas读取文件路径或文件名称包含中文的csv文件方法
2018/07/04 Python
Python multiprocess pool模块报错pickling error问题解决方法分析
2019/03/20 Python
windows系统中Python多版本与jupyter notebook使用虚拟环境的过程
2019/05/15 Python
CSS3制作彩色进度条样式的代码示例分享
2016/06/23 HTML / CSS
写演讲稿所需要注意的4个条件
2014/01/09 职场文书
教师求职信范文
2014/05/24 职场文书
教师群众路线剖析材料
2014/09/29 职场文书
关于感谢信的范文
2015/01/23 职场文书
工作失职检讨书范文
2015/05/05 职场文书
2015年七七事变78周年纪念活动方案
2015/05/06 职场文书
红色影片观后感
2015/06/18 职场文书
Nginx访问日志及错误日志参数说明
2021/03/31 Servers
python not运算符的实例用法
2021/06/30 Python