Flask使用Flask-SQLAlchemy
管理数据库,安装方式:
$ pip install flask-sqlalchemy
在 Flask-SQLAlchemy 中,数据库使用 URL 指定:
MySQL mysql://username:password@hostname/database
Postgres postgresql://username:password@hostname/database
SQLite( Unix) sqlite:////absolute/path/to/database
SQLite( Windows) sqlite:///c:/absolute/path/to/database
程序使用的数据库 URL
必须保存到 Flask
配置对象的 SQLALCHEMY_DATABASE_URI
键中。
配置对象中还有一个很有用的选项, 即 SQLALCHEMY_COMMIT_ON_TEARDOWN
键,将其设为 True
时,每次请求结束后都会自动提交数据库中的变动。
初始化以及配置一个简单的SQLite
数据库:
from os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
在 ORM 中,模型一般是一个 Python 类,类中的属性对应数据库表中的列。
下面定义两个表对应的Python类。
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User', backref='role')
def __repr__(self):
return '' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username=db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return '' % self.username
类变量 __tablename__
定义在数据库中使用的表名。如果没有定义 __tablename__
, Flask-SQLAlchemy
会使用一个默认名字,但默认的表名没有遵守使用复数形式进行命名的约定,所以最好由我们自己来指定表名。其余的类变量都是该模型的属性,被定义为 db.Column
类的实例。
Flask-SQLAlchemy 要求每个模型都要定义主键,这一列经常命名为 id。
上面的User
类和Role
类中,通过下面的代码定义了两个类的关系:
class Role(db.Model):
# ...
users = db.relationship('User', backref='role', lazy='dynamic')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
之前定义了两个表的名字:
users
和roles
。
关系使用 users
表中的外键连接了两行。添加到 User
模型中的 role_id
列被定义为外键, 就是这个外键建立起了关系。传给 db.ForeignKey()
的参数 'roles.id'
表明,这列的值是 roles
表中行的 id
值。
添加到 Role
模型中的 users
属性代表这个关系的面向对象视角。对于一个 Role
类的实例,其 users
属性将返回与角色相关联的用户组成的列表。db.relationship()
的第一个参数表明这个关系的另一端是哪个模型。
db.relationship()
中的 backref
参数向 User
模型中添加一个 role
属性,从而定义反向关系。这一属性可替代 role_id
访问 Role
模型,此时获取的是模型对象,而不是外键的值。
其中参数lazy
的作用是:禁止自动查询,当在一个Role
对象中调用users
属性的时候,会自动执行query
,返回所有的属于该Role
对象的User
对象,因此无法在此结果上附加更精确的查询过滤器。例如原来这样直接获取User
对象users = user_role.users
,增加lazy
参数后这样获取:user_role.users.order_by(User.username).all()
,增加了更精确的过滤器。
在该部分实现数据库的创建、初始化等工作,所以把这部分代码放在manage.py
文件中:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask_script import Manager, Server, Shell
from somepackage import app, db
from somepackage import User, Role
manager = Manager(app)
@manager.command
def init_db():
"""
Init the DB and Insert test data
"""
# this db is a SQLAlchemy instance, must be imported.
# the create_all method is belonged to SQLAlchemy instance.
db.create_all()
admin_role = Role(id=1, name="Admin")
user_role = Role(id=2, name="User")
user_jhon = User(id=1, username="Jhon", role=admin_role)
user_david = User(id=2, username="david", role=user_role)
db.session.add_all([admin_role, user_role, user_jhon, user_david])
try:
db.session.commit()
except Exception, e:
db.session.rollback()
print e
if __name__ == "__main__":
manager.run()
注意:其中的
User
和Role
,以及db
都要从合适的包或者模块中引入进来。
这样就能够创建并初始化数据库。下面看SQLAlchemy提供的过滤器和查询函数:
其中
filter
和filter_by
的区别就是:filter_by
之后的条件直接是字段=查询的值
。filter
之后的条件是类名.字段=查询条件
。
通过上面的配置就可以在视图函数中操作数据库了:
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username = form.name.data)
db.session.add(user)
session['known'] = False
else:
session['known'] = True
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html',
form = form,
name = session.get('name'),
known = session.get('known', False)