NoSQL Database
- collections:tables
- documents:records
少了很多关系,多了很多单独的表.不再指向一个数据源,修改起来会麻烦.
对join的支持很差
5.4 python数据库框架
Flask-SQLAlchemy
$pip install 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
EN p53 CHS p47
配置数据库:
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)
5.6 定义数据库模型
用类来表达数据库结构,底层已经被抽象了。
主键通常为id列
虽然没有强制要求,但这两个模型都定义了 repr() 方法,返回一个具有可读性的字符
串表示模型,可在调试和测试时使用。
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
列数据类型,查表
列选项,主键,唯一等
5.7 关系
一个role对应多个user,外键
backref定义反向引用关系,这一属性(见下节中代码)可替代 role_id 访问 Role 模型,此时获取的是模型对象,而不是外键的值
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
多种关系选项,可查表 p49
一对一:uselist false
多对多,需要用到第三张表.
5.8 数据库操作
$ python app.py shell
5.8.1 增
db.create_all()
from hello import Role, User
admin_role = Role(name='Admin')
mod_role = Role(name='Moderator')
user_role = Role(name='User')
在user设置role,上节中的backref:
user_john = User(username='john', role=admin_role)
user_susan = User(username='susan', role=user_role)
user_david = User(username='david', role=user_role)
这些新建对象的 id
属性并没有明确设定,因为主键是由 Flask-SQLAlchemy 管理的。现在这些对象只存在于
Python 中,还未写入数据库。因此 id 尚未赋值
通过数据库会话管理对数据库所做的改动,在 Flask-SQLAlchemy 中,会话由 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.add_all([admin_role, mod_role, user_role,user_john, user_susan, user_david])
为了把对象写入数据库,我们要调用 commit() 方法提交会话:
db.session.commit()
会话的作用:保证数据库一致性.
和git的使用很像.
5.8.2 删
db.session.delete(mod_role)
db.session.commit()
5.8.3 查
Role.query.all()
User.query.filter_by(role=user_role).all()
5.8.4 改
在数据库会话上调用 add() 方法也能更新模型。我们继续在之前的 shell 会话中进行操作,
下面这个例子把 “Admin” 角色重命名为 “Administrator” :
>>> admin_role.name = 'Administrator'
>>> db.session.add(admin_role)
>>> db.session.commit()
要查看 SQLAlchemy 为查询生成的原生 SQL 查询语句,只需把 query 对象转换成字符串:
>>> 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'
常用过滤器:
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit() 使用指定的值限制原查询返回的结果数量,返回一个新查询
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询
在查询上应用指定的过滤器后,通过调用 all() 执行查询,以列表的形式返回结果。
查询执行函数,查表.
- all() 以列表形式返回查询的所有结果
- first() 返回查询的第一个结果,如果没有结果,则返回 None
- first_or_404() 返回查询的第一个结果,如果没有结果,则终止请求,返回 404 错误响应
- get() 返回指定主键对应的行,如果没有对应的行,则返回 None
- get_or_404() 返回指定主键对应的行,如果没找到指定的主键,则终止请求,返回 404 错误响应
- count() 返回查询结果的数量
- paginate() 返回一个 Paginate 对象,它包含指定范围内的结果
在定义关系时候加上lazy = ‘dynamic’ 延迟查询的进行从而可以在其上添加新的过滤器.
5.9 在视图函数中操作数据库
app.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))
index.html的模板也做相应修改.
5.10 启动python shell自动导入数据库
from flask.ext.script import Shell
#make_shell_context() 函数注册了程序、数据库实例以及模型,因此这些对象能直接导入flask-script的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))
5.11 修改数据库模型
先删除,再建立,会丢失数据
migration框架.可以跟踪数据库模型的变化
相当于数据库的git,记录变更历程。
除 了 直 接 使 用 Alembic 之 外,Flask 程 序 还 可 使 用 Flask-Migrate(http://flask-migrate.readthedocs.org/en/latest/)扩展。这个扩展对 Alembic 做了轻量级包装,并集成到 Flask-Script 中,所有操作都通过 Flask-Script 命令完成。
(venv) $ pip install flask-migrate
初始化,附加一个db命令用来调用migrate:
from flask.ext.migrate import Migrate, MigrateCommand
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
在维护数据库迁移之前,要使用 init 子命令创建迁移仓库:
(venv) $ python hello.py db init
migrate 子命令用来自动创建迁移脚本:
(venv) $ python hello.py db migrate -m “initial migration”
upgrade() 函数把迁移中的改动应用到数据库中, downgrade() 函数则将改动删除。
(venv) $ python hello.py db upgrade