SQL Databases
特点是什么?(得空补充)
NoSQL Databases
特点是什么?(得空补充)
SQL or NoSQL?
相比各自的优势是什么?(得空补充)
Python Database Frameworks
-
你可以使用哪些数据库
不管是否开源的数据库,Flask都有对应数据库引擎的package比如:MySQL、 Postgres、 SQLite、 Redis、MongoDB 或 CouchDB。 -
你还可以使用什么
如果这些还不够,还有一些数据库抽象层package比如SQLAlchemy、 MongoEngine能让你在更高的层次操作数据(以对象形式)而非以table、document、query languages的形式。 -
选择数据库有哪些参考依据
- 易用性:SQL Vs NoSQL, 后者当然完胜了
- 效率:后者在对象到数据库模型转化中会有一定开销,但这可以忽略不计,反过来,后者对生成率的促进远超过造成的开销。
- 可移植性:尽管很多数据库抽象层只提供对一种数据库的支持,但是有的更高层次的数据库抽象层几乎支持所有的数据库,比如:SQLAlchemy ORM。
- Flask集成:如果能以Flask的extension的形式存在意味着可以省去很多手写的代码。
-
本书选择的数据库
综上,本书选择Flask-SQLAlchemy(作为SQLAlchemy的扩展)作为数据库工具。
使用Flask-SQLAlchemy进行数据库管理
Flask-SQLAlchemy是一个使用了SQLAlchemy的扩展。 SQLAlchemy 是一个强大的关系型数据库框架,它能够支持多种数据库并提供了高层的ORM和底层的原生数据库操作。
- 安装
(venv) $ pip install flask-sqlalchemy
-
数据库URL
在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/databasehostname对应一台主机,username和password好理解,对于sqlite数据库是没有用户名密码的,所以它只是目录下的一个文件而已。
-
配置
数据库URL被在配置在: SQLALCHEMY_DATABASE_URI,还有另一个重要的属性被配置在SQLALCHEMY_COMMIT_ON_TEARDOWN( 用来在每次请求结束时候提交数据库改动,通常设置为True)。 -
实例
如下是一个配置SQLite数据库的例子, db实例化了一个SQLAlchemy对象并提供了所有Flask-SQLAlchemy具备的功能:
import os
#..
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)
Model定义
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
def __repr__(self):
return '<Role %r>' % 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)
def __repr__(self):
return '<User %r>' % self.username
类变量 __tablename__ 定义了表名(尽管默认会有名字,但是是非复数形式的不太好),所有的属性都定义为db.Column的实例对象,db.Column的第一个参数是类别第二个参数是可选配置参数(所有table都要有primary key),__repr__()方法是为了方便调试和测试用。
Relationships
如下示例展示了一个一对多的关系(对于配置的理解不深先照样写吧!),该书附录了一张表格列出了db.relationship常用的配置参数说明,需要时参考:
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
Database操作
学习这些数据库操作的的最好办法是在Python Shell中,如下几个部分将开始学习最常见的数据库操作(本章前面的构建model的代码应用到hello.py中):
# shell方式需要安装flask-script,create_all会创建所有的model
(venv) $ python hello.py shell
>>> from hello import db
>>> db.create_all()
#重新设计表结构以后需要drop掉之前的表
>>> db.drop_all()
>>> db.create_all()
>>> from hello import Role, User
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)
# 因为没有commit所有的对象都还没有id
>>> print(admin_role.id) None
>>> print(mod_role.id) None
>>> print(user_role.id) None
# 用db.session管理对象的持续化
>>> db.session.add(admin_role)
>>> db.session.add(mod_role)
>>> db.session.add(user_role)
>>> db.session.add(user_john)
>>> db.session.add(user_susan)
>>> db.session.add(user_david)
>>> db.session.commit()
# 数据已经提交带数据库
>>> print(admin_role.id) 1
>>> print(mod_role.id) 2
>>> print(user_role.id) 3
# 修改属性
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()
# 删除
>>> db.session.delete(mod_role)
>>> db.session.commit()
# 查询
>>> Role.query.all()
[<Role u'Administrator'>, <Role u'User'>]
>>> User.query.all()
[<User u'john'>, <User u'susan'>, <User u'david'>]
# 过滤器
>>> User.query.filter_by(role=user_role).all()
[<User u'susan'>, <User u'david'>]
# 查询方法和过滤器是多种多样的,该书列出了两个表格可供查询,参考page61.
# 还可以获取到原生的查询语句
>>> str(User.query.filter_by(role=user_role))
'SELECT users.id AS users_id, users.username AS users_username,
users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id'
# 注意,关闭了shell窗口以后,意味着你要重新import db、Role,重新构建之前定义过的对象,如下:
>>> from hello import db
>>> from hello import Role
>>> user_role = Role.query.filter_by(name='User').first()
# 关联查询
>>> users = user_role.users
>>> users
[<User u'susan'>, <User u'david'>]
>>> users[0].role
<Role u'User'>
# 如上查询,如果想要应用filter是不行的。因为user_role.usres已经调用了all()方法,要使用filter需要在py中进行修改:
class Role(db.Model):
# ...
users = db.relationship('User', backref='role', lazy='dynamic')
# ...
>>> user_role.users.order_by(User.username).all()
[<User u'david'>, <User u'susan'>]
>>> user_role.users.count()
2
Python Shell集成Model
如果每次打开Shell都要手动导入未免太繁琐了,Flask-Script提供了配置可以自动导入对象:
from flask.ext.script import Shell
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))
在View Functions中操作数据库
将数据库操作应用到View Function中,如下是一个例子:
index.py
@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))
templates/index.html
{% extends "commonBase.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1>
{% if not known %}
<p>Pleased to meet you!</p>
{% else %}
<p>Happy to see you again!</p>
{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
使用Flask-Migrate来做数据库的Migrations
开发进行到一定阶段,你会发现model的结构需要发生改变,Flask-SQLAlchemy从Model来构建数据库表结构只会发生在以前表不存在的时候,当然你也可以不管数据丢失先删除数据库。
更好的做法是使用数据库迁移框架,就像代码能够进行版本控制一样,一个数据库迁移框架能够跟踪数据库的变化,并且渐进的应用数据库的改变。
SQLAlchemy的开发者写了一个名叫Alembic的框架,但是我们并不打算直接使用它,而是使用Flask-Migrate extension扩展来和Flask-Script集成,全部通过命令行来达到目的。
Creating a Migration Repository
(venv) $ pip install flask-migrate
如下展示而来该扩展的配置方式:
from flask.ext.migrate import Migrate, MigrateCommand
# ...
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
使用db的子命令来构建一个资源库:
(venv) $ python index.py db init
Creating a Migration Script
Alembic migrations能够有手动和自动两种模式可用。
手动的migration要创建空的工具方法upgrade()和downgrade(),自动migration会自动查找当前数据库和model definitions的不同之处来完成upgrade()和downgrade()。
一个自动 migration 的例子:
(venv) $ python index.py db migrate -m "initial migration"
Upgrading the Database
一旦migration完成,你就可以通过db upgrade 来更新数据库了,你可以把data.sqlite删除以后再执行命令。
(venv) $ python hello.py db upgrade
贯穿全书都会围绕migration来推进,本书只是简单介绍,后面有更详细的内容。
网友评论
(virtualenv) E:\Dome\Django\item\test_1>python manager.py db migrate -m 'initial migration'
usage: manager.py [-?] {shell,deploy,db,dev,runserver,test} ...
manager.py: error: too many arguments