SQLAlchemy是一个ORM(对象关系映射)。基于对目标数据库的原生SQL的抽象,提供了一串和数据库引擎一致的API。这一列表中包括MySQL,PostgreSQL,和SQLite。这使得在你的模型和数据库间交换数据变得轻松愉快,同时也使得诸如换掉数据库引擎和迁移数据库模式等其他事情变得没那么繁琐。

Flask-SQLAIchemyFlask使用SQLAlchemy插件。它为SQLAlchemy设置了许多合理的配置。它也内置一些session管理,这样你就不用在应用代码处理一些基础的事务。

安装

1
pip install -u flask_sqlalchemy pymysql

配置

Flask应用启动前,需要给Flask-SQLAlchemy加载一些配置项已定义它的行为规则。
有些配置在Flask引擎创建后就不能修改,所以你应该在一个应用创建前将这些配置项加载进Flask-SQLAlchemy。

一般我们将这些数据库配置项放在instance文件夹中,在初始化时需要加上instance_relative_config。例如:

1
2
3
4
5
6
7
8
9
10
11
  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__, instance_relative_config=True)

app.config.from_object('config')
app.config.from_pyfile('config.py')

db = SQLAlchemy(app)

instance/config.py:

1
2
3
4
5
6
7
8
9
10
11
  
SQLALCHEMY_DATABASE_URI = "mysql+pymysql://username:password@server/db" #用于连接数据的数据库

SQLALCHEMY_ECHO = "True" #如果设置成 True,SQLAlchemy 将会记录所有 发到标准输出(stderr)的语句,这对调试很有帮助。

SQLALCHEMY_POOL_SIZE = 10 # 数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)。

SQLALCHEMY_TRACK_MODIFICATIONS = True

SQLALCHEMY_POOL_TIMEOUT = 100 # 指定数据库连接池的超时时间(秒)。默认是10

声明模型

Flask-SQLAlchemy中的模型就是继承了db.Model的子类,最后它会转为数据库的tableSQLAlchemy会根据你在模型类提供的信息进行适当的mapper()调用。

有一些声明在SQLAlchemy上是必选的,但是在Flask-SQLAlchemy上是可选的。比如表名是自动地为你设置好多的,除非你想覆盖他。Flask-SQLAlchemy会将类名转换为小写,会将CamelCase驼峰命名方式转为camel_case。如果你想自定义表名可以在class中设置__tablename__属性值。

1
2
3
4
5
6
7
8
  
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

def __repr__(self):
return '<User %r>' % self.username

通过Column定义表中字段规则。

  • id - 变量名就是字段名。
  • db.Integer - 第一个参数是字段类型。
  • primary_key=True - 设置为主键,如果有多个主键将变成联合主键。
  • unique=True - 字段值是唯一的
  • nullable=False - 字段值不能为空
类型 描述
Integer 一个整数
String (size) 有长度限制的字符串
Text 一些较长的 unicode 文本
DateTime 表示为 Python datetime 对象的 时间和日期
Float 存储浮点值
Boolean 存储布尔值
PickleType 存储为一个持久化的 Python 对象
LargeBinary 存储一个任意大的二进制数据

一对多关系

Flask-SQLAlchemy中不同表关联处理和定义是通过relationshop()方法来实现。
如果是要关联外键需要通过ForeignKey

1
2
3
4
5
6
7
8
9
10
11
  
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
addresses = db.relationship('Address', backref='person', lazy=True)

class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), nullable=False)
person_id = db.Column(db.Integer, db.ForeignKey('person.id'),
nullable=False)

db.relationship()

提供两个类之间关系的映射。第一个参数是映射的类名,这个类可以是当时还没创建的。

  • backref - 是一个在 Address 类上声明新属性的简单方法。你可以使用 my_address.person 来获取使用该地址(address)的人(person)。
  • lazy - 决定了 SQLAlchemy 什么时候从数据库中加载数据
    • ‘select’/True (默认值) 就是说 SQLAlchemy 会使用一个标准的 select 语句必要时一次加载数据。
    • ‘joinded’/False 告诉 SQLAlchemy 使用 JOIN 语句作为父级在同一查询中来加载关系。
    • ‘subquery’ 类似 ‘joined’ ,但是 SQLAlchemy 会使用子查询。
    • ‘dynamic’ 在有多条数据的时候是特别有用的。不是直接加载这些数据,SQLAlchemy 会返回一个查询对象,在加载数据前您可以过滤(提取)它们。

通过backref()函数定义backref字段查询方式:

1
2
3
4
5
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
addresses = db.relationship('Address', lazy='select',
backref=db.backref('person', lazy='joined'))

CRUD

当创建了模型类,我们在交互式的Python shell中导入db对象并且调用SQLAlchemy.create_all() 方法来创建表和数据库:

1
2
>>> from yourapplication import db
>>> db.create_all()

有了库和表我们就可以进行CRUD(Create,Retrieve,Update,Delete)操作。

Create

添加数据分为三步:

  • 根据模型类创建一个Python对象
  • 将对象添加到会话中
  • 提交会话
1
2
3
4
>>> from yourapp import User
>>> me = User('admin', 'admin@example.com')
>>> db.session.add(me)
>>> db.session.commit()

在对象添加到会话之前(调用db.session.add()),你可以放弃改变。当添加到会话后,会向数据库发送INSERT声明,但因为事务没有正式提交你还获取不到ID返回。

Retrieve

Flask-SQLAlchemy为每个模型类提供了query属性用于查询。query其实是个对象里面提供了丰富的查询过滤的方法。

id username email
1 admin admin@example.com
2 peter peter@example.org
3 guest guest@example.com
1
2
3
4
5
>>> peter = User.query.filter_by(username='peter').first()
>>> peter.id
2
>>> peter.email
u'peter@example.org

判断用户名是否存在:

1
2
3
>>> missing = User.query.filter_by(username='missing').first()
>>> missing is None
True

通过模糊条件信息查询用户

1
2
>>> User.query.filter(User.email.endswith('@example.com')).all()
[<User u'admin'>, <User u'guest'>]

通过批量条件查询用户

1
2
>>> User.query.filter(User.id.in_(['23','25'])).all()
[<User u'admin'>, <User u'guest'>]

排序根据用户名:

1
2
>>> User.query.order_by(User.username).all()
[<User u'admin'>, <User u'guest'>, <User u'peter'>]

限制查询:

1
2
>>> User.query.limit(1).all()
[<User u'admin'>]

根据主键查询:

1
2
>>> User.query.get(1)
<User u'admin'>

在视图中查询,我们可用get_or_404()代替get(),用first_or_404()代替first()方法。它会解析404错误代替返回None:

1
2
3
4
@app.route('/user/<username>')
def show_user(username):
user = User.query.filter_by(username=username).first_or_404()
return render_template('show_user.html', user=user)

Update

更新数据前,先根据条件取的对象在手动修改提交。例如:

1
2
3
4
5
6
7
admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email@example.com'
db.session.commit()

user = User.query.get(5)
user.name = 'New Name'
db.session.commit()

Delete

删除很简单,先查询对的到对象然后用delete()删除。

1
2
3
admin = User.query.filter_by(username='admin').first()
db.session.delete(admin)
db.session.commit()