SQLAlchemy是python的一个数据库ORM工具,提供了强大的对象模型间的转换,可以满足绝大多数数据库操作的需求,并且支持多种数据库引擎(sqlite,mysql,postgres, mongodb等),在这里记录基本用法和学习笔记。

一、安装

通过pip安装

  1. $ pip install SQLAlchemy

二、使用

首先是连接到数据库,SQLALchemy支持多个数据库引擎,不同的数据库引擎连接字符串不一样,常用的有

  1. mysql://username:password@hostname/database
  2. postgresql://username:password@hostname/database
  3. sqlite:absolute/path/to/database
  4. sqlite:///c:/absolute/path/to/database

更多连接字符串的介绍参见这里

下面是连接和使用sqlite数据库的例子

1. connection

使用传统的connection的方式连接和操作数据库

  1. from sqlalchemy import create_engine
  2. # 数据库连接字符串
  3. DB_CONNECT_STRING = 'sqlite:///:memory:'
  4. # 创建数据库引擎,echo为True,会打印所有的sql语句
  5. engine = create_engine(DB_CONNECT_STRING, echo=True)
  6. # 创建一个connection,这里的使用方式与python自带的sqlite的使用方式类似
  7. with engine.connect() as con:
  8. # 执行sql语句,如果是增删改,则直接生效,不需要commit
  9. rs = con.execute('SELECT 5')
  10. data = rs.fetchone()[0]
  11. print "Data: %s" % data

与python自带的sqlite不同,这里不需要Cursor光标,执行sql语句不需要commit

2. connection事务

使用事务可以进行批量提交和回滚

  1. from sqlalchemy import create_engine
  2. # 数据库连接字符串
  3. DB_CONNECT_STRING = 'sqlite:Users/zhengxiankai/Desktop/Document/db.sqlite'
  4. engine = create_engine(DB_CONNECT_STRING, echo=True)
  5. with engine.connect() as connection:
  6. trans = connection.begin()
  7. try:
  8. r1 = connection.execute("select * from User")
  9. r2 = connection.execute("insert into User(name, age) values(?, ?)", 'bomo', 24)
  10. trans.commit()
  11. except:
  12. trans.rollback()
  13. raise

3. session

connection是一般使用数据库的方式,sqlalchemy还提供了另一种操作数据库的方式,通过session对象,session可以记录和跟踪数据的改变,在适当的时候提交,并且支持强大的ORM的功能,下面是基本使用

  1. from sqlalchemy import create_engine
  2. from sqlalchemy.orm import sessionmaker
  3. # 数据库连接字符串
  4. DB_CONNECT_STRING = 'sqlite:Users/zhengxiankai/Desktop/Document/db.sqlite'
  5. # 创建数据库引擎,echo为True,会打印所有的sql语句
  6. engine = create_engine(DB_CONNECT_STRING, echo=True)
  7. # 创建会话类
  8. DB_Session = sessionmaker(bind=engine)
  9. # 创建会话对象
  10. session = DB_Session()
  11. # dosomething with session
  12. # 用完记得关闭,也可以用with
  13. session.close()

上面创建了一个session对象,接下来可以操作数据库了,session也支持通过sql语句操作数据库

  1. session.execute('select * from User')
  2. session.execute("insert into User(name, age) values('bomo', 13)")
  3. session.execute("insert into User(name, age) values(:name, :age)", {'name': 'bomo', 'age':12})
  4. # 如果是增删改,需要commit
  5. session.commit()

注意参数使用dict,并在sql语句中使用:key占位

4. ORM

上面简单介绍了sql的简单用法,既然是ORM框架,我们先定义两个模型类User和Role,sqlalchemy的模型类继承自一个由declarative_base()方法生成的类,我们先定义一个模块Models.py生成Base类

  1. from sqlalchemy.ext.declarative import declarative_base
  2. Base = declarative_base()

User.py

  1. from sqlalchemy import Column, Integer, String
  2. from Models import Base
  3. class User(Base):
  4. __tablename__ = 'User'
  5. id = Column('id', Integer, primary_key=True, autoincrement=True)
  6. name = Column('name', String(50))
  7. age = Column('age', Integer)

Role.py

  1. from sqlalchemy import Column, Integer, String
  2. from Models import Base
  3. class Role(Base):
  4. __tablename__ = 'Role'
  5. id = Column('id', Integer, primary_key=True, autoincrement=True)
  6. name = Column('name', String(50))

从上面很容易看出来,这里的模型对应数据库中的表,模型支持的类型有Integer, String, Boolean, Date, DateTime, Float,更多类型包括类型对应的Python的类型参见:这里

Column构造函数相关设置

  • name:名称
  • type_:列类型
  • autoincrement:自增
  • default:默认值
  • index:索引
  • nullable:可空
  • primary_key:外键

更多介绍参见这里

接下来通过session进行增删改查

  1. from sqlalchemy import create_engine
  2. from sqlalchemy.orm import sessionmaker
  3. from User import User
  4. from Role import Role
  5. from Models import Base
  6. DB_CONNECT_STRING = 'sqlite:Users/zhengxiankai/Desktop/Document/db.sqlite'
  7. engine = create_engine(DB_CONNECT_STRING, echo=True)
  8. DB_Session = sessionmaker(bind=engine)
  9. session = DB_Session()
  10. # 1. 创建表(如果表已经存在,则不会创建)
  11. Base.metadata.create_all(engine)
  12. # 2. 插入数据
  13. u = User(name = 'tobi', age = 200)
  14. r = Role(name = 'user')
  15. # 2.1 使用add,如果已经存在,会报错
  16. session.add(u)
  17. session.add(r)
  18. session.commit()
  19. print r.id
  20. # 3 修改数据
  21. # 3.1 使用merge方法,如果存在则修改,如果不存在则插入
  22. r.name = 'admin'
  23. session.merge(r)
  24. # 3.2 也可以通过这种方式修改
  25. session.query(Role).filter(Role.id == 1).update({'name': 'admin'})
  26. # 4. 删除数据
  27. session.query(Role).filter(Role.id == 1).delete()
  28. # 5. 查询数据
  29. # 5.1 返回结果集的第二项
  30. user = session.query(User).get(2)
  31. # 5.2 返回结果集中的第2-3项
  32. users = session.query(User)[1:3]
  33. # 5.3 查询条件
  34. user = session.query(User).filter(User.id < 6).first()
  35. # 5.4 排序
  36. users = session.query(User).order_by(User.name)
  37. # 5.5 降序(需要导入desc方法)
  38. from sqlalchemy import desc
  39. users = session.query(User).order_by(desc(User.name))
  40. # 5.6 只查询部分属性
  41. users = session.query(User.name).order_by(desc(User.name))
  42. for user in users:
  43. print user.name
  44. # 5.7 给结果集的列取别名
  45. users = session.query(User.name.label('user_name')).all()
  46. for user in users:
  47. print user.user_name
  48. # 5.8 去重查询(需要导入distinct方法)
  49. from sqlalchemy import distinct
  50. users = session.query(distinct(User.name).label('name')).all()
  51. # 5.9 统计查询
  52. user_count = session.query(User.name).order_by(User.name).count()
  53. age_avg = session.query(func.avg(User.age)).first()
  54. age_sum = session.query(func.sum(User.age)).first()
  55. # 5.10 分组查询
  56. users = session.query(func.count(User.name).label('count'), User.age).group_by(User.age)
  57. for user in users:
  58. print 'age:{0}, count:{1}'.format(user.age, user.count)
  59. session.close()

5. 多表关系

上面的所有操作都是基于单个表的操作,下面是多表以及关系的使用,我们修改上面两个表,添加外键关联(一对多和多对一)

User模型

  1. from sqlalchemy import Column, Integer, String
  2. from sqlalchemy import ForeignKey
  3. from sqlalchemy.orm import relationship
  4. from Models import Base
  5. class User(Base):
  6. __tablename__ = 'users'
  7. id = Column('id', Integer, primary_key=True, autoincrement=True)
  8. name = Column('name', String(50))
  9. age = Column('age', Integer)
  10. # 添加角色id外键(关联到Role表的id属性)
  11. role_id = Column('role_id', Integer, ForeignKey('roles.id'))
  12. # 添加同表外键
  13. second_role_id = Column('second_role_id', Integer, ForeignKey('roles.id'))
  14. # 添加关系属性,关联到role_id外键上
  15. role = relationship('Role', foreign_keys='User.role_id', backref='User_role_id')
  16. # 添加关系属性,关联到second_role_id外键上
  17. second_role = relationship('Role', foreign_keys='User.second_role_id', backref='User_second_role_id')

Role模型

  1. from sqlalchemy import Column, Integer, String
  2. from sqlalchemy.orm import relationship
  3. from Models import Base
  4. class Role(Base):
  5. __tablename__ = 'roles'
  6. id = Column('id', Integer, primary_key=True, autoincrement=True)
  7. name = Column('name', String(50))
  8. # 添加关系属性,关联到User.role_id属性上
  9. users = relationship("User", foreign_keys='User.role_id', backref="Role_users")
  10. # 添加关系属性,关联到User.second_role_id属性上
  11. second_users = relationship("User", foreign_keys='User.second_role_id', backref="Role_second_users")

这里有一点需要注意的是,设置外键的时候ForeignKey('roles.id')这里面使用的是表名和表列,在设置关联属性的时候relationship('Role', foreign_keys='User.role_id', backref='User_role_id'),这里的foreign_keys使用的时候类名和属性名

接下来就可以使用了

  1. u = User(name='tobi', age=200)
  2. r1 = Role(name='admin')
  3. r2 = Role(name='user')
  4. u.role = r1
  5. u.second_role = r2
  6. session.add(u)
  7. session.commit()
  8. # 查询(对于外键关联的关系属性可以直接访问,在需要用到的时候session会到数据库查询)
  9. roles = session.query(Role).all()
  10. for role in roles:
  11. print 'role:{0} users'
  12. for user in role.users:
  13. print '\t{0}'.format(user.name)
  14. print 'role:{0} second_users'
  15. for user in role.second_users:
  16. print '\t{0}'.format(user.name)

上面表示的是一对多(多对一)的关系,还有一对一,多对多,如果要表示一对一的关系,在定义relationship的时候设置uselist为False(默认为True),如在Role中

  1. class Role(Base):
  2. ...
  3. user = relationship("User", uselist=False, foreign_keys='User.role_id', backref="Role_user")

6. 多表查询

多表查询通常使用join进行表连接,第一个参数为表名,第二个参数为条件,例如

  1. users = db.session.query(User).join(Role, Role.id == User.role_id)
  2. for u in users:
  3. print u.name

join为内连接,还有左连接outerjoin,用法与join类似,右连接和全外链接在1.0版本上不支持,通常来说有这两个结合查询的方法基本够用了,1.1版本貌似添加了右连接和全外连接的支持,但是目前只是预览版

还可以直接查询多个表,如下

  1. result = db.session.query(User, Role).filter(User.role_id = Role.id)
  2. # 这里选择的是两个表,使用元组获取数据
  3. for u, r in result:
  4. print u.name

三、数据库迁移

sqlalchemy的数据库迁移/升级有两个库支持alembic和sqlalchemy-migrate

由于sqlalchemy-migrate在2011年发布了0.7.2版本后,就已经停止更新了,并且已经不维护了,也积累了很多bug,而alembic是较后来才出现,而且是sqlalchemy的作者开发的,有良好的社区支持,所以在这里只学习alembic这个库

alembic实现了类似git/svn的版本管理的控制,我们可以通过alembic维护每次升级数据库的版本

1. 安装

通过pip安装,pip会自动安装相关的依赖

  1. $ pip install alembic

2. 初始化

安装完成后再项目根目录运行

  1. yourproject/
  2. alembic.ini
  3. YOUR_ALEMBIC_DIR/
  4. env.py
  5. README
  6. script.py.mako
  7. versions/
  8. 3512b954651e_add_account.py
  9. 2b1ae634e5cd_add_order_id.py
  10. 3adcc9a56557_rename_username_field.py

其中

  • alembic.ini 提供了一些基本的配置
  • env.py 每次执行Alembic都会加载这个模块,主要提供项目Sqlalchemy Model 的连接
  • script.py.mako 迁移脚本生成模版
  • versions 存放生成的迁移脚本目录

默认情况下创建的是基于单个数据库的,如果需要支持多个数据库或其他,可以通过alembic list_templates查看支持的模板

  1. $ alembic list_templates
  2. Available templates:
  3. generic - Generic single-database configuration.
  4. multidb - Rudimentary multi-database configuration.
  5. pylons - Configuration that reads from a Pylons project environment.
  6. Templates are used via the 'init' command, e.g.:
  7. alembic init --template generic ./scripts

3. 配置

使用之前,需要配置一下链接字符串,打开alembic.ini文件,设置sqlalchemy.url连接字符串,例如

  1. sqlalchemy.url = sqlite:Users/zhengxiankai/Desktop/database.db

其他参数可以参见官网说明:http://alembic.zzzcomputing.com/en/latest/tutorial.html

4. 创建数据库版本

接下来我们创建一个数据库版本,并新建两个表

  1. $ alembic revision -m 'create table'

创建一个版本(会在yourproject/YOUR_ALEMBIC_DIR/versions/文件夹中创建一个python文件1a8a0d799b33_create_table.py)

该python模块包含upgrade和downgrade两个方法,在这里添加一些新增表的逻辑

  1. """create table
  2. Revision ID: 4fd533a56b34
  3. Revises:
  4. Create Date: 2016-09-18 17:20:27.667100
  5. """
  6. from alembic import op
  7. import sqlalchemy as sa
  8. # revision identifiers, used by Alembic.
  9. revision = '4fd533a56b34'
  10. down_revision = None
  11. branch_labels = None
  12. depends_on = None
  13. def upgrade():
  14. # 添加表
  15. op.create_table(
  16. 'account',
  17. sa.Column('id', sa.Integer, primary_key=True),
  18. sa.Column('name', sa.String(50), nullable=False),
  19. sa.Column('description', sa.Unicode(200)),
  20. )
  21. # 添加列
  22. # op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))
  23. def downgrade():
  24. # 删除表
  25. op.drop_table('account')
  26. # 删除列
  27. # op.drop_column('account', 'last_transaction_date')

这里使用到了了op对象,关于op对象的更多API使用,参见这里

5. 升级数据库

刚刚实现了升级和降级的方法,通过下面命令升级数据库到最新版本

  1. $ alembic upgrade head

这时候可以看到数据库多了两个表alembic_version和account,alembic_version存放数据库版本

关于升级和降级的其他命令还有下面这些

  1. # 升到最高版本
  2. $ alembic upgrade head
  3. # 降到最初版本
  4. $ alembic downgrade base
  5. # 升两级
  6. $ alembic upgrade +2
  7. # 降一级
  8. $ alembic downgrade -1
  9. # 升级到制定版本
  10. $ alembic upgrade e93b8d488143
  11. # 查看当前版本
  12. $ alembic current
  13. # 查看历史版本详情
  14. $ alembic history --verbose
  15. # 查看历史版本(-r参数)类似切片
  16. $ alembic history -r1975ea:ae1027
  17. $ alembic history -r-3:current
  18. $ alembic history -r1975ea:

6. 通过元数据升级数据库

上面我们是通过API升级和降级,我们也可以直接通过元数据更新数据库,也就是自动生成升级代码,先定义两个Model(User,Role),这里我定义成三个文件

  1. yourproject/
  2. YOUR_ALEMBIC_DIR/
  3. tutorial/Db
  4. Models.py
  5. User.py
  6. Role.py

代码就放在一起了

  1. from sqlalchemy.ext.declarative import declarative_base
  2. from sqlalchemy import Column, Integer, String
  3. Base = declarative_base()
  4. class User(Base):
  5. __tablename__ = 'users'
  6. id = Column('id', Integer, primary_key=True, autoincrement=True)
  7. name = Column('name', String)
  8. class Role(Base):
  9. __tablename__ = 'roles'
  10. id = Column('id', Integer, primary_key=True, autoincrement=True)
  11. name = Column('name', String)

在YOUR_ALEMBIC_DIR/env.py配置元数据

  1. target_metadata = None

改为

  1. import os
  2. import sys
  3. # 这里需要添加相对路径到sys.path,否则会引用失败,尝试过使用相对路径,但各种不好使,还是使用这种方法靠谱些
  4. sys.path.append(os.path.abspath(os.path.join(os.getcwd(), "../yourproject/tutorial/Db")))
  5. from User import User
  6. from Role import Role
  7. from Models import Base
  8. target_metadata = Base.metadata

os.path.join(os.getcwd()这个获取到的地址不是env.py的路径,而是根目录

在创建数据库版本的时候添加--autogenerate参数,就会从Base.metadata元数据中生成脚本

  1. $ alembic revision --autogenerate -m "add user table"

这时候会在生成升级代码

  1. """add user table
  2. Revision ID: 97de1533584a
  3. Revises: 8678ab6d48c1
  4. Create Date: 2016-09-19 21:58:00.758410
  5. """
  6. from alembic import op
  7. import sqlalchemy as sa
  8. # revision identifiers, used by Alembic.
  9. revision = '97de1533584a'
  10. down_revision = '8678ab6d48c1'
  11. branch_labels = None
  12. depends_on = None
  13. def upgrade():
  14. ### commands auto generated by Alembic - please adjust! ###
  15. op.create_table('roles',
  16. sa.Column('id', sa.Integer(), nullable=False),
  17. sa.Column('name', sa.String(), nullable=True),
  18. sa.PrimaryKeyConstraint('id')
  19. )
  20. op.create_table('users',
  21. sa.Column('id', sa.Integer(), nullable=False),
  22. sa.Column('name', sa.String(), nullable=True),
  23. sa.PrimaryKeyConstraint('id')
  24. )
  25. op.drop_table('account')
  26. ### end Alembic commands ###
  27. def downgrade():
  28. ### commands auto generated by Alembic - please adjust! ###
  29. op.create_table('account',
  30. sa.Column('id', sa.INTEGER(), nullable=False),
  31. sa.Column('name', sa.VARCHAR(length=50), nullable=False),
  32. sa.Column('description', sa.VARCHAR(length=200), nullable=True),
  33. sa.Column('last_transaction_date', sa.DATETIME(), nullable=True),
  34. sa.PrimaryKeyConstraint('id')
  35. )
  36. op.drop_table('users')
  37. op.drop_table('roles')
  38. ### end Alembic commands ###

由于我没有定义account模型,会被识别为删除,如果删除了model的列的声明,则会被识别为删除列,自动生成的版本我们也可以自己修改,然后执行升级命令即可升级alembic upgrade head

需要注意的是

  1. Base.metadata声明的类必须以数据库中的一一对应,如果数据库中有的表,而在元数据中没有,会识别成删除表
  2. revision创建版本之前执行之前需要升级到最新版本
  3. 配置Base之前,需要保证所有的Model都已经执行(即导入)过一次了,否则无法读取到,也就是需要把所有Model都import进来

数据库升级有风险,升级前最好先检查一遍upgrade函数,可以的话做好备份哈

四、常见问题

1. String长度问题

如果使用mysql数据库,String类型对应的是VARCHAR类型,需要指定长度,否则会报下面错误,而在sqlite不会出现

  1. (in table 'user', column 'name'): VARCHAR requires a length on dialect mysql

TODO:如有其他问题欢迎留言

五、参考链接

  • Auto Generating Migrations
  • tutorial
作者:bomo
来源:51CTO

Python数据库ORM工具sqlalchemy的学习笔记相关推荐

  1. 《深度学习入门-基于Python的理论与实现》学习笔记1

    <深度学习入门-基于Python的理论与实现>学习笔记1 第一章Python入门 Python是一个简单.易读.易记的编程语言,可以用类似于英语的语法进行编写程序,可读性高,且能写出高性能 ...

  2. Python的dict字典结构操作方法学习笔记

    Python的dict字典结构操作方法学习笔记 这篇文章主要介绍了Python的dict字典结构操作方法学习笔记本,字典的操作是Python入门学习中的基础知识,需要的朋友可以参考下 一.字典的基本方 ...

  3. 23神经网络 :唐宇迪《python数据分析与机器学习实战》学习笔记

    唐宇迪<python数据分析与机器学习实战>学习笔记 23神经网络 1.初识神经网络 百度深度学习研究院的图,当数据规模较小时差异较小,但当数据规模较大时深度学习算法的效率明显增加,目前大 ...

  4. 关于《Python绝技:运用Python成为顶级黑客》的学习笔记

    本篇文章主要把<Python绝技:运用Python成为顶级黑客>中的代码敲一遍,学学Python安全相关的编程与思路,然后根据具体的情况修改一下代码. 第一章--入门 1.准备开发环境 安 ...

  5. Python爬虫、数据分析、可视化学习笔记(一、梦开始的地方)

    系列文章持续更新中...... 文章目录 一.工具及环境配置 1.Python3.8(官网下载) 2.Pycharm(官网下载) 3.Anaconda(官网下载) 4.环境配置(传送门,感谢前人栽树) ...

  6. python标识符最大可能长度_Opencv-Python学习笔记(二)

    2. 使用OpenCV3处理图像 2.1 不同色彩空间的转换 OpenCV中有数百种关于在不同色彩空间之间转换的方法. 三种常用色彩空间:灰度.BGR.HSV(Hue色调,Saturation饱和度, ...

  7. oracle数据库修改写入状态,【学习笔记】Oracle oradebug 使用oradebug修改数据库SCN方法案例...

    天萃荷净 使用oradebug修改数据库scn,使用oradebug修改数据库scn的案例. 这里也做了两个测试,发现该功能确实很巧妙,通过修改内存中的scn值,然后写入控制文件和数据文件,实现修改s ...

  8. python编程16章教程_Python学习笔记__16.2章 TCP编程

    # 这是学习廖雪峰老师python教程的学习笔记 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Socket需要知道目标计算 ...

  9. python编程语言继承_python应用:学习笔记(Python继承)

    学习笔记(Python继承)Python是一种解释型脚本语言,可以应用于以下领域: web 和 Internet开发 科学计算和统计 人工智能 教育 桌面界面开发 后端开发 网络爬虫 有几种叫法(父类 ...

最新文章

  1. SAP SD基础知识之现金销售
  2. linux/windows下安装scala
  3. Matlab绘图基础
  4. cmd上运行java程序遇到的问题(找不到或无法加载主类)
  5. android lrc 歌词显示,Android歌词 AndroidLrc歌词
  6. 排序算法部分知识点小结
  7. WCF中NetTCp配置
  8. c语言上机作业题及答案,2017计算机二级C语言上机测试题附答案
  9. 使用PowerDesigner设计数据库保姆级教程
  10. 【专题5: 硬件设计】 之 【70.开关电源 之 电感计算例子】
  11. hdu 5857 Median(模拟)
  12. 格式工厂 wav 比特率_这可能是 Mac 上最强格式转换工具
  13. 云服务器搭建青龙面板每日自动拿京豆
  14. JSHOP2与ROS通信
  15. lazysnapping
  16. PhantomJS将网页保存为图片
  17. 解决idea不检查语法错误问题
  18. 如何检查Mac配备的显卡(GPU)?
  19. 三伏天如何祛除“月子病”?
  20. 云服务器安装kafka及python连接测试

热门文章

  1. 浅谈JavaScript中的String类型
  2. 有货基于Kubernetes容器环境的持续交付实践
  3. GAMES101-计算机图形学-作业2
  4. scpi指令转换c语言,SCPI 标准命令
  5. android源代码查看(在线观看,AndroidStudio,SourceInsight)
  6. 蓝桥杯_嵌入式:蜂鸣器(库函数)
  7. 判断IE10以下提示更换浏览器
  8. figma设计用户头像效果
  9. kali扩展内存(全)
  10. 公司买的dsx2-5000网线测试仪有一年多了,需要进行校准吗?