学习flask-5-数据库

就是数据库啊~

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