在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 相关文章推荐
Java Web开发过程中登陆模块的验证码的实现方式总结
May 25 Python
python+matplotlib实现动态绘制图片实例代码(交互式绘图)
Jan 20 Python
python如何生成各种随机分布图
Aug 27 Python
python中单例常用的几种实现方法总结
Oct 13 Python
Python 访问限制 private public的详细介绍
Oct 16 Python
Python告诉你木马程序的键盘记录原理
Feb 02 Python
python命令行工具Click快速掌握
Jul 04 Python
py-charm延长试用期限实例
Dec 22 Python
python读取Kafka实例
Dec 23 Python
python GUI库图形界面开发之PyQt5不规则窗口实现与显示GIF动画的详细方法与实例
Mar 09 Python
Opencv求取连通区域重心实例
Jun 04 Python
Python进阶学习之带你探寻Python类的鼻祖-元类
May 08 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
PHP中调用JAVA
2006/10/09 PHP
使用php+apc实现上传进度条且在IE7下不显示的问题解决方法
2013/04/25 PHP
php+mysql实现无限分类实例详解
2015/01/15 PHP
js修改table中Td的值(定义td的双击事件)
2013/01/10 Javascript
阻止子元素继承父元素事件具体思路及实现
2013/05/02 Javascript
JQuery-tableDnD 拖拽的基本使用介绍
2013/07/04 Javascript
javascript中的变量作用域以及变量提升详细介绍
2013/10/24 Javascript
js返回上一页并刷新的多种实现方法
2014/02/26 Javascript
jQuery实现自动与手动切换的滚动新闻特效代码分享
2015/08/27 Javascript
基于Javascript实现弹出页面效果
2016/01/01 Javascript
JS实现控制文本框的内容
2016/07/10 Javascript
Bootstrap和Angularjs配合自制弹框的实例代码
2016/08/24 Javascript
protractor的安装与基本使用教程
2017/07/07 Javascript
浅谈webpack打包生成的bundle.js文件过大的问题
2018/02/22 Javascript
详解element-ui设置下拉选择切换必填和非必填
2019/06/17 Javascript
jquery 插件重新绑定的处理方法分析
2019/11/23 jQuery
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
2021/01/05 Vue.js
[01:23:45]DOTA2-DPC中国联赛 正赛 CDEC vs Dragon BO3 第一场 1月22日
2021/03/11 DOTA
Django的HttpRequest和HttpResponse对象详解
2018/01/26 Python
Python列表生成式与生成器操作示例
2018/08/01 Python
python样条插值的实现代码
2018/12/17 Python
python 字符串追加实例
2019/07/20 Python
Python API自动化框架总结
2019/11/12 Python
Django使用消息提示简单的弹出个对话框实例
2019/11/15 Python
简单了解python数组的基本操作
2019/11/26 Python
python基于TCP实现的文件下载器功能案例
2019/12/10 Python
Python中的全局变量如何理解
2020/06/04 Python
python框架flask入门之环境搭建及开启调试
2020/06/07 Python
Python经典五人分鱼实例讲解
2021/01/04 Python
HTML5实现的图片无限加载的瀑布流效果另带边框圆角阴影
2014/03/07 HTML / CSS
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
韩国流行时尚女装网站:Dintchina(中文)
2018/07/19 全球购物
小学优秀学生评语
2014/12/29 职场文书
2015年教育实习工作总结
2015/04/24 职场文书
什么是创业计划书?什么是商业计划书?这里一一解答
2019/07/12 职场文书
Java方法重载和方法重写的区别到底在哪?
2021/06/11 Java/Android