Python的Django框架中使用SQLAlchemy操作数据库的教程


Posted in Python onJune 02, 2016

零、SQLAlchemy是什么?
SQLAlchemy的官网上写着它的介绍文字:

SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives
application developers the full power and flexibility of SQL.
SQLAlchemy 是一个非常强大的ORM和数据库工具,但是它庞大的文档和复杂的功能总是让很 多人望而生畏。而Django的ORM相对来说就让很多人觉得简单实用。

事实上,SQLAlchemy其实也没有那么复杂,光使用它一些比较高级的功能其实并没有比 使用Django ORM复杂多少,而它丰富的功能则能让你在遇到更复杂的问题时处理起来得心应手。

写作本文的主要目的在于:

  • 通过对比SQLAlchemy ORM和Django ORM的主要使用方法, 尽量简单直观的让Django用户能够快速了解和上手SQLAlchemy这款强大的工具。
  • 不牵扯到SQLAlchemy具体的技术细节,包括Engine连接池、Session的具体工作原理等等

SQLAlchemy相对于Django内建的ORM来说,有几处非常明显的优点:

  • 可独立使用,任何使用Python的项目都可以用它来操作数据库
  • 和直接使用原始的DBAPI相比,提供了非常丰富的特性:连接池、auto-map等等
  • 提供了更底层的SQL抽象语言,能用原始sql解决的问题基本上都可以用SQLAlchemy解决
  • 接下来我们针对日常的数据库操作来对比一下Django ORM和SQLAlchemy。

文中使用的 SQLAlchemy 版本为 0.9.8

一、Django VS SQLAlchemy

SQLAlchemy的安装:

wget http://peak.telecommunity.com/dist/ez_setup.py
 python ez_setup.py
 sudo easy_install sqlalchemy
 sudo easy_install ipython

1.建立数据表

首先,我们需要先建立几个表。

(1)Django

在Django中,如果要建表,就是在models.py中定义你的数据类型:

from django.db import models

class Game(models.Model):
 ... ...

class GameCompany(models.Model):
 ... ...

因为文章主要面向有经验的Django用户,所以此处不写出详细的定义代码。定义Model以后 我们还需要在settings.py中DATABASES处设置需要连接的数据库地址。最后,使用syncdb来 完成数据库表的创建。

(2)SQLAlchemy

在SQLAlchemy中,定义表结构的过程和Django类似:

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, Date
from sqlalchemy.orm import relationship, backref

Base = declarative_base()

# 定义表结构
class GameCompany(Base):
 __tablename__ = 'game_company'

 id = Column(Integer, primary_key=True)
 name = Column(String(200), nullable=False)
 country = Column(String(50))


class Game(Base):
 __tablename__ = 'game'

 id = Column(Integer, primary_key=True)
 company_id = Column(Integer, ForeignKey('game_company.id'), index=True)
 category = Column(String(10))
 name = Column(String(200), nullable=False)
 release_date = Column(Date)

 # 和Django不同,外键需要显式定义,具体好坏见仁见智
 # 此处的relation可以为lazy加载外键内容时提供一些可配置的选项
 company = relationship('GameCompany', backref=backref('games'))


# 此处定义要使用的数据库
engine = create_engine('mysql://root:root@localhost:5379/sqlalchemy_tutorial?charset=utf8')
# 调用create_all来创建表结构,已经存在的表将被忽略
Base.metadata.create_all(engine)

2.插入一些数据

接下来,我们往表中插入一些数据

(1)Django

Django中比较常用的插入数据方法就是使用 .save() 了。

nintendo = GameCompany(name="nintendo", country="Japan")
nintendo.save()

game1 = Game(
 company=nintendo,
 category="ACT",
 name="Super Mario Bros",
 release_date='1985-10-18')
game1.save()

# 或者使用create
Game.objects.create(... ...)

(2)SQLAlchemy

在SQLAlchemy ORM中,有一个非常关键的对象 session ,所有对于数据的操作都是 通过session来进行的,所以要插入数据之前,我们得先初始化一个session:

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()

之后插入数据的方法也和Django比较相似:

# 添加数据
nintendo = GameCompany(name="Nintendo", country="Japan")
capcom = GameCompany(name="Capcom", country="Japan")
game1 = Game(
 company=nintendo,
 category="ACT",
 name="Super Mario Bros",
 release_date='1985-10-18'
)
game2 = Game(
 company=capcom,
 category="ACT",
 name="Devil May Cry 3: Dante's Awakening",
 release_date="2005-03-01",
)
game3 = Game(
 company=nintendo,
 category="RPG",
 name="Mario & Luigi: Dream Team",
 release_date="2013-08-11",
)

# 使用add_all来让这些objects和session产生关系
session.add_all([nintendo, capcom, game1, game2])
# 在没有开启autocommit的模式下,不要忘了调用commit来让数据写到数据库中
session.commit()

除了commit之外,session还有rollback()等方法,你可以把session对象简单看成是一次 transaction,所以当你对内容进行修改时,需要调用 session.commit() 来提交这些修改。

去文档可以了解更多session相关内容:http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html

二、常用操作

1.简单查询

(1)批量查询

# -- Django --
Game.objects.filter(category="RPG")

# -- SQLAlchemy --
# 使用filter_by是和django ORM比较接近的方式
session.query(Game).filter_by(category="RPG")
session.query(Game).filter(Game.category == "RPG")

(2)查询单个对象

# -- Django --
Game.objects.get(name="Super Mario Bros")

# -- SQLAlchemy --
session.query(Game).filter_by(name="Super Mario Bros").one()
# `get_objects_or_None()`
session.query(Game).filter_by(name="Super Mario Bros").scalar()

Django中得各种 > 、< 都是使用在字段名称后面追加 "__gt"、"__lt" 来实现的,在SQLAlchemy 中这样的查询还要更直观一些

# -- Django --
Game.objects.filter(release_date__gte='1999-01-01')
# 取反
Game.objects.exclude(release_date__gte='1999-01-01')

# -- SQLAlchemy --
session.query(Game).filter(Game.release_date >= '1999-01-01').count()
# 取反使用 ~ 运算符
session.query(Game).filter(~Game.release_date >= '1999-01-01').count()
通过外键组合查询

# -- Django --
Game.objecs.filter(company__name="Nintendo")

# -- SQLAlchemy --
session.query(Game).join(GameCompany).filter(GameCompany.name == "Nintendo")

2.多条件或查询

# -- Django --
from django.db.models import Q
Game.objects.filter(Q(category="RPG") | Q(category="ACT"))

# -- SQLAlchemy --
from sqlalchemy import or_
session.query(Game).filter(or_(Game.category == "RPG", Game.category == "ACT"))
session.query(Game).filter((Game.category == "RPG") | (Game.category == "ACT"))

(1)in查询

# -- Django --
Game.objects.filter(category__in=["GAL", "ACT"])

# -- SQLAlchemy --
session.query(Game).filter(Game.category.in_(["GAL", "ACT"]))

(2)like查询

# -- Django --
Game.objects.filter(name__contains="Mario")

# -- SQLAlchemy --
session.query(Game.name.contains('Mario'))

3.统计个数

简单统计总数:

# -- Django --
Game.objects.filter(category="RPG").count()

# -- SQLAlchemy --
session.query(Game).filter_by(category="RPG").count()
分组统计个数

# -- Django --
from django.db.models import Count
Game.objects.values_list('category').annotate(Count('pk')).order_by()

# -- SQLAlchemy --
from sqlalchemy import func
session.query(Game.category, func.count(Game.category)).group_by(Game.category).all()

4.结果排序

对查询结果进行排序:

# -- Django --
Game.objects.all().order_by('release_date')
Game.objects.all().order_by('-release_date')
# 多字段排序
Game.objects.all().order_by('-release_date', 'category')

# -- SQLAlchemy --
session.query(Game).order_by(Game.release_date)
session.query(Game).order_by(Game.release_date.desc())
# 多字段排序
session.query(Game).order_by(Game.release_date.desc(), Game.category)

5.修改数据

# -- Django --
game = Game.objects.get(pk=1)
game.name = 'Super Mario Brothers'
game.save()

# -- SQLAlchemy --
game = session.query(Game).get(1)
game.name = 'Super Mario Brothers'
session.commit()

6.批量修改

# -- Django --
Game.objects.filter(category="RPG").update(category="ARPG")

# -- SQLAlchemy --
session.query(Game).filter_by(category="RPG").update({"category": "ARPG"})

7.批量删除

# -- Django --
Game.objects.filter(category="ARPG").delete()

# -- SQLAlchemy --
session.query(Game).filter_by(category="ARPG").delete()

三、SQLAlchemy其他一些值得关注的功能
上面简单列了一些SQLAlchemy ORM和Django ORM的使用方法对比,SQLAlchemy同时还提供了一些 其他非常有用的功能,比如Automap~

假如你有一个Django项目,通过ORM创建了一大堆Model。这时来了一个新项目,需要操作 这些表,应该怎么办?拷贝这些Models?使用原始的DB-API加上sql来操作?

其实使用SQLAlchemy的Automap可以让你的工作变得非常的方便,你只要在新项目连接到旧数据库,然后 稍微配置一下Automap,就可以使用SQLAlchemy的ORM操作那些通过别的系统创建的表了。

就像这样:

from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine

Base = automap_base()
engine = create_engine("sqlite:///mydatabase.db")
Base.prepare(engine, reflect=True)

# user和address就是表明,通过这样的语句就可以把他们分别映射到User和Address类
User = Base.classes.user
Address = Base.classes.address

更多信息可以参考详细文档:http://docs.sqlalchemy.org/en/rel_0_9/orm/extensions/automap.html

附:Django与SQLAlchemy结合的实例演示
譬如,以下gumi/db.py代码,其中gumi制作Django项目名,项目中使用的唯一的数据库连接的包装,作为py调用。

# -*- coding: utf-8 -*- 
from django.conf import settings 
from django.core import signals 
from django.dispatch import dispatcher 
import sqlalchemy 
from sqlalchemy.orm import scoped_session, sessionmaker 
from sqlalchemy.engine.url import URL 
 
__all__ = ['Session', 'metadata'] 
 
def create_engine(): 
 url = URL(drivername=settings.DATABASE_ENGINE, 
    database=settings.DATABASE_NAME, 
    username=settings.DATABASE_USER, 
    password=settings.DATABASE_PASSWORD, 
    host=settings.DATABASE_HOST, 
    port=settings.DATABASE_PORT or None, 
    query = getattr(settings, 'DATABASE_OPTIONS', {}) 
    ) 
 
 options = getattr(settings, 'SQLALCHEMY_OPTIONS', {}) 
 engine = sqlalchemy.create_engine(url, **options) 
 return engine 
 
def end_request(signal, sender): 
 Session.remove() 
 
dispatcher.connect(receiver=end_request, 
     signal=signals.request_finished) 
 
metadata = sqlalchemy.MetaData() 
 
Session = scoped_session(sessionmaker(autoflush=True, 
          transactional=True, 
          bind=create_engine()))

模块代码

from sqlalchemy.orm import * 
from gumi.db import Session, metadata 
some_table = Table('some_table', metadata, 
       Column('id', Integer, primary_key=True), 
       Column('some_value', String(100), nullable=False, 
       mysql_engine='InnoDB', 
       ) 
class SomeObject(object): 
 pass 
mapper(SomeObject, some_table)

视图代码

import django.newforms as forms 
from gumi.db import Session 
 
class SomeForm(forms.Form): 
  # newform 
  pass 
 
def some_action(req): 
  if req.method != "POST": 
   form = SomeForm() 
  else: 
   form = SomeForm(req.POST) 
   if form.is_valid(): 
     data = form.clean() 
     obj = SomeObject() 
     obj.some_param = data['a'] 
     obj.another_param = data['b'] 
     Session.save(obj) 
     Session.commit() 
     return HttpResponseRedirect('/') 
  return render_to_response('some/template.html')
Python 相关文章推荐
python实现获取序列中最小的几个元素
Sep 25 Python
Pythont特殊语法filter,map,reduce,apply使用方法
Feb 27 Python
对于Python中RawString的理解介绍
Jul 07 Python
详解python使用turtle库来画一朵花
Mar 21 Python
简单了解python gevent 协程使用及作用
Jul 22 Python
python实现局域网内实时通信代码
Dec 22 Python
Tensorflow的常用矩阵生成方式
Jan 04 Python
详解python itertools功能
Feb 07 Python
基于python爬取有道翻译过程图解
Mar 31 Python
Python写捕鱼达人的游戏实现
Mar 31 Python
keras Lambda自定义层实现数据的切片方式,Lambda传参数
Jun 11 Python
Python requests接口测试实现代码
Sep 08 Python
实例解析Python中的__new__特殊方法
Jun 02 #Python
详解Python中的__new__、__init__、__call__三个特殊方法
Jun 02 #Python
Python实现优先级队列结构的方法详解
Jun 02 #Python
KMP算法精解及其Python版的代码示例
Jun 01 #Python
Python缩进和冒号详解
Jun 01 #Python
Python注释详解
Jun 01 #Python
深入理解python try异常处理机制
Jun 01 #Python
You might like
深入分析php中接口与抽象类的区别
2013/06/08 PHP
执行、获取远程代码返回:file_get_contents 超时处理的问题详解
2013/06/25 PHP
php旋转图片90度的方法
2013/11/07 PHP
php给图片添加文字水印方法汇总
2015/08/27 PHP
基础的WordPress插件制作教程
2015/11/24 PHP
验证坐标在某坐标区域内php代码
2016/10/08 PHP
PHP实现的XML操作类【XML Library】
2016/12/29 PHP
php设计模式之模板模式实例分析【星际争霸游戏案例】
2020/03/24 PHP
PHP+Redis事务解决高并发下商品超卖问题(推荐)
2020/08/03 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
MooTools 页面滚动浮动层智能定位实现代码
2011/08/23 Javascript
JS实现时间格式化的方式汇总
2013/10/16 Javascript
浅析offsetLeft,Left,clientLeft之间的区别
2013/11/30 Javascript
JS弹出层单纯的绝对定位居中示例代码
2014/02/18 Javascript
jQuery中queue()方法用法实例
2014/12/29 Javascript
js函数与php函数的区别实例浅析
2015/01/12 Javascript
基于JavaScript实现瀑布流布局(二)
2016/01/26 Javascript
利用Query+bootstrap和js两种方式实现日期选择器
2017/01/10 Javascript
jquery实现tab键进行选择后enter键触发click行为
2017/03/29 jQuery
Vue2.0 UI框架ElementUI使用方法详解
2017/04/14 Javascript
ES6知识点整理之对象解构赋值应用示例
2019/04/17 Javascript
python自定义解析简单xml格式文件的方法
2015/05/11 Python
在主机商的共享服务器上部署Django站点的方法
2015/07/22 Python
利用python操作SQLite数据库及文件操作详解
2017/09/22 Python
win10下python3.5.2和tensorflow安装环境搭建教程
2018/09/19 Python
对pandas写入读取h5文件的方法详解
2018/12/28 Python
Python设计模式之工厂方法模式实例详解
2019/01/18 Python
关于Pytorch的MLP模块实现方式
2020/01/07 Python
详解HTML5中的picture元素响应式处理图片
2018/01/03 HTML / CSS
华三通信H3C面试题
2015/05/15 面试题
机电一体化职业规划书
2014/01/07 职场文书
《在大海中永生》教学反思
2014/02/24 职场文书
售后服务承诺书
2014/03/26 职场文书
社会治安综合治理责任书
2015/01/29 职场文书
诚实守信主题班会
2015/08/13 职场文书
Python+Selenium实现抖音、快手、B站、小红书、微视、百度好看视频、西瓜视频、微信视频号、搜狐视频、一点号、大风号、趣头条等短视频自动发布
2022/04/13 Python