flask 扩展(一)

988

Flask-Script 扩展命令行

from flask import Flask
from flask_script import Manager

app = Flask(__name__)

manager = Manager(app)


@app.route('/')
def index():
    return '我的快乐时代'


if __name__ == "__main__":
    manager.run()

通过使用Flask-Script扩展,我们可以在Flask服务器启动的时候,通过命令行的方式传入参数。而不仅仅通过app.run()方法中传参,

比如我们可以通过python hello.py runserver --host ip地址,告诉服务器在哪个网络接口监听来自客户端的连接。默认情况下,服务器只监听来自服务器所在计算机发起的连接,即localhost连接。

我们可以通过 python hello.py runserver --help 来查看参数:

Flask-WTF

相关参考链接

github.com/luhuisicnu/…

关于 web 表单

web表单是web应用程序的基本功能。 它是HTML页面中负责数据采集的部件。表单有三个部分组成:表单标签、表单域、表单按钮。表单允许用户输入数据,负责HTML页面数据采集,通过表单将用户输入的数据提交给服务器。

在Flask中,为了处理web表单,我们一般使用Flask-WTF扩展,它封装了WTForms,并且它有验证表单数据的功能。

WTForms支持的HTML标准字段

WTForms常用验证函数

表单中的 CSRF

使用Flask-WTF需要配置参数SECRET_KEY。 CSRF_ENABLED是为了CSRF(跨站请求伪造)保护。 SECRET_KEY用来生成加密令牌,当CSRF激活的时候,该设置会根据设置的密匙生成加密令牌。

在HTML页面中直接写form表单的模式

文件结构:

templates/login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <form method='post'>
        <input type="text" name="username" placeholder='Username'>
        <input type="password" name="password" placeholder='password'>
        <input type="submit">
    </form>
</body>
</html>

hello.py

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        print(username, password)

    return render_template('login.html', method=request.method)


if __name__ == "__main__":
    app.run()

使用Flask-WTF实现表单

安装:

pip install flask-wtf

首先需要配置参数: app.config['SECRET_KEY'] = 'ruiyang'

文件结构:

hello.py

from flask import Flask, render_template, redirect, url_for, request, flash

from flask_wtf import Form
from wtforms import SubmitField, StringField, PasswordField
from wtforms.validators import DataRequired, EqualTo

app = Flask(__name__)
app.config['SECRET_KEY'] = 'ruiyang'


class Login(Form):
    us = StringField(label=u'用户:', validators=[DataRequired()])
    ps = PasswordField(label=u'密码', validators=[DataRequired(),EqualTo('ps2','err')])
    ps2 = PasswordField(label=u'确认密码', validators=[DataRequired()])
    submit = SubmitField(u'提交')


@app.route('/', methods=['GET', 'POST'])
def index():
    form = Login()
    if form.validate_on_submit():
        name = form.us.data
        pswd = form.ps.data
        pswd2 = form.ps2.data
        print(name, pswd, pswd2)
        return "login seccess"
    else:
        if request.method == 'POST':
            flash(u'信息有误,请重新输入!')
        print(form.validate_on_submit())

    return render_template('login.html', form=form)


if __name__ == '__main__':
    app.run(debug=True)

templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <form method="post">
            {{ form.csrf_token() }}
            {{ form.us.label }}
            <p>{{ form.us }}</p>
            {{ form.ps.label }}
            <p>{{ form.ps }}</p>
            {{ form.ps2.label }}
            <p>{{ form.ps2 }}</p>
            <p>{{ form.submit() }}</p>
            {% for x in get_flashed_messages() %}
                {{ x }}
            {% endfor %}
     </form>
</body>
</html>

flask-sqlalchemy

相关参考链接

github.com/luhuisicnu/…

安装

pip install flask-sqlalchemy

需要连接mysql数据库,仍然需要安装 flask-mysqldb

pip install flask-mysqldb

配置

Flask的数据库设置:

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:ruiyang@127.0.0.1:3306/test'

常用的SQLAlchemy字段类型

常用的SQLAlchemy列选项

常用的SQLAlchemy关系选项

在程序中的基础操作

创建测试数据:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)

# 设置连接数据库的URL
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:ruiyang@127.0.0.1:3306/test'
# 设置每次请求结束后会自动提交数据库中的改动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)


class Role(db.Model):
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    us = db.relationship('User', backref='role')

    def __repr__(self):
        return 'Role:{}'.format(self.name)


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(64), unique=True)
    pswd = db.Column(db.String(64))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return 'User:{}'.format(self.name)


if __name__ == '__main__':
    db.drop_all()
    db.create_all()
    ro1 = Role(name='admin')
    ro2 = Role(name='user')
    db.session.add_all([ro1, ro2])
    db.session.commit()
    us1 = User(name='wang', email='wang@163.com', pswd='123456', role_id=ro1.id)
    us2 = User(name='zhang', email='zhang@189.com', pswd='201512', role_id=ro2.id)
    us3 = User(name='chen', email='chen@126.com', pswd='987654', role_id=ro2.id)
    us4 = User(name='zhou', email='zhou@163.com', pswd='456789', role_id=ro1.id)
    db.session.add_all([us1, us2, us3, us4])
    db.session.commit()
    app.run(debug=True)

运行程序之后启动 flask shell 进行操作:

创建和删除表:

>>> db.create_all()
2019-08-10 13:45:02,631 INFO sqlalchemy.engine.base.Engine DESCRIBE `roles`
2019-08-10 13:45:02,631 INFO sqlalchemy.engine.base.Engine ()
2019-08-10 13:45:02,632 INFO sqlalchemy.engine.base.Engine DESCRIBE `users`
2019-08-10 13:45:02,632 INFO sqlalchemy.engine.base.Engine ()
>>> db.drop_all()
2019-08-10 13:45:11,267 INFO sqlalchemy.engine.base.Engine DESCRIBE `roles`
2019-08-10 13:45:11,267 INFO sqlalchemy.engine.base.Engine ()
2019-08-10 13:45:11,268 INFO sqlalchemy.engine.base.Engine DESCRIBE `users`
2019-08-10 13:45:11,268 INFO sqlalchemy.engine.base.Engine ()
2019-08-10 13:45:11,270 INFO sqlalchemy.engine.base.Engine 
DROP TABLE users
2019-08-10 13:45:11,270 INFO sqlalchemy.engine.base.Engine ()
2019-08-10 13:45:11,283 INFO sqlalchemy.engine.base.Engine COMMIT
2019-08-10 13:45:11,284 INFO sqlalchemy.engine.base.Engine 
DROP TABLE roles
2019-08-10 13:45:11,284 INFO sqlalchemy.engine.base.Engine ()
2019-08-10 13:45:11,294 INFO sqlalchemy.engine.base.Engine COMMIT

插入单条数据:

>>> from hello import Role
>>> role_admin = Role(name="admin") 
>>> db.session.add(role_admin) 
>>> db.session.commit()
2019-08-10 13:46:40,071 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-10 13:46:40,071 INFO sqlalchemy.engine.base.Engine INSERT INTO roles (name) VALUES (%s)
2019-08-10 13:46:40,072 INFO sqlalchemy.engine.base.Engine ('admin',)
2019-08-10 13:46:40,073 INFO sqlalchemy.engine.base.Engine COMMIT
>>> role1 = Role(name="user1") 
>>> db.session.add(role1) 
>>> db.session.commit()
2019-08-10 13:47:03,975 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-10 13:47:03,975 INFO sqlalchemy.engine.base.Engine INSERT INTO roles (name) VALUES (%s)
2019-08-10 13:47:03,975 INFO sqlalchemy.engine.base.Engine ('user1',)
2019-08-10 13:47:03,976 INFO sqlalchemy.engine.base.Engine COMMIT
>>> role2 = Role(name="ruiyang") 
>>> db.session.add(role2) 
>>> db.session.commit()
2019-08-10 13:47:39,205 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-10 13:47:39,205 INFO sqlalchemy.engine.base.Engine INSERT INTO roles (name) VALUES (%s)
2019-08-10 13:47:39,205 INFO sqlalchemy.engine.base.Engine ('ruiyang',)
2019-08-10 13:47:39,206 INFO sqlalchemy.engine.base.Engine COMMIT

插入多条数据: add_all()

>>> us1 = User(name='wang',email='wang@163.com',pswd='123456',role_id=role_admin.id)
2019-08-10 13:49:09,064 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-10 13:49:09,064 INFO sqlalchemy.engine.base.Engine SELECT roles.id AS roles_id, roles.name AS roles_name 
FROM roles 
WHERE roles.id = %s
2019-08-10 13:49:09,065 INFO sqlalchemy.engine.base.Engine (1,)
>>> us2 = User(name='zhang',email='zhang@189.com',pswd='201512',role_id=role1.id)
2019-08-10 13:49:52,577 INFO sqlalchemy.engine.base.Engine SELECT roles.id AS roles_id, roles.name AS roles_name 
FROM roles 
WHERE roles.id = %s
2019-08-10 13:49:52,577 INFO sqlalchemy.engine.base.Engine (2,)
>>> us3 = User(name='chen',email='chen@126.com',pswd='987654',role_id=role1.id)
>>> us4 = User(name='zhou',email='zhou@163.com',pswd='456789',role_id=role2.id)
2019-08-10 13:50:20,853 INFO sqlalchemy.engine.base.Engine SELECT roles.id AS roles_id, roles.name AS roles_name 
FROM roles 
WHERE roles.id = %s
2019-08-10 13:50:20,853 INFO sqlalchemy.engine.base.Engine (3,)
>>> db.session.add_all([us1,us2,us3,us4])
>>> db.session.commit()
2019-08-10 13:50:38,442 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, email, pswd, role_id) VALUES (%s, %s, %s, %s)
2019-08-10 13:50:38,442 INFO sqlalchemy.engine.base.Engine ('wang', 'wang@163.com', '123456', 1)
2019-08-10 13:50:38,444 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, email, pswd, role_id) VALUES (%s, %s, %s, %s)
2019-08-10 13:50:38,444 INFO sqlalchemy.engine.base.Engine ('zhang', 'zhang@189.com', '201512', 2)
2019-08-10 13:50:38,445 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, email, pswd, role_id) VALUES (%s, %s, %s, %s)
2019-08-10 13:50:38,445 INFO sqlalchemy.engine.base.Engine ('chen', 'chen@126.com', '987654', 2)
2019-08-10 13:50:38,446 INFO sqlalchemy.engine.base.Engine INSERT INTO users (name, email, pswd, role_id) VALUES (%s, %s, %s, %s)
2019-08-10 13:50:38,446 INFO sqlalchemy.engine.base.Engine ('zhou', 'zhou@163.com', '456789', 3)
2019-08-10 13:50:38,446 INFO sqlalchemy.engine.base.Engine COMMIT

使用 filter_by 精确查询: 查询第一个和全部

>>> User.query.filter_by(name='wang').first()
2019-08-10 13:54:08,112 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name = %s 
 LIMIT %s
2019-08-10 13:54:08,113 INFO sqlalchemy.engine.base.Engine ('wang', 1)
User:wang
>>> User.query.filter_by(name='wang').all()
2019-08-10 13:54:15,698 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name = %s
2019-08-10 13:54:15,698 INFO sqlalchemy.engine.base.Engine ('wang',)
[User:wang]

使用 filter 模糊查询,返回结尾字符为 g 的所有数据:

>>> User.query.filter(User.name.endswith('g')).all()
2019-08-12 09:01:24,998 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE (users.name LIKE concat('%%', %s))
2019-08-12 09:01:24,999 INFO sqlalchemy.engine.base.Engine ('g',)
[User:wang, User:zhang]

逻辑非:返回名字不等于 wang 的全部数据:

>>> User.query.filter(User.name!='wang').all()
2019-08-12 09:02:03,321 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %s
2019-08-12 09:02:03,321 INFO sqlalchemy.engine.base.Engine ('wang',)
[User:zhang, User:chen, User:zhou]

逻辑与,需要导入 _and 返回and() 条件满足的所有数据:

>>> User.query.filter(and_(User.name != 'wang',User.email.endswith('163.com'))).all()
2019-08-12 09:22:21,840 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %s AND (users.email LIKE concat('%%', %s))
2019-08-12 09:22:21,841 INFO sqlalchemy.engine.base.Engine ('wang', '163.com')
[User:zhou]

逻辑或,需要导入 or_

>>> from sqlalchemy import or_
>>> User.query.filter(or_(User.name != "wang", User.email.endswith('163.com'))).all()
2019-08-12 09:24:17,577 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %s OR (users.email LIKE concat('%%', %s))
2019-08-12 09:24:17,577 INFO sqlalchemy.engine.base.Engine ('wang', '163.com')
[User:wang, User:zhang, User:chen, User:zhou]

not_ 相当于取反

>>> from sqlalchemy import not_
>>> User.query.filter(not_(User.name == "chen")).all()
2019-08-12 09:25:49,716 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.name != %s
2019-08-12 09:25:49,716 INFO sqlalchemy.engine.base.Engine ('chen',)
[User:wang, User:zhang, User:zhou]

查询数据后删除

>>> user = User.query.first()
2019-08-12 09:26:58,334 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
 LIMIT %s
2019-08-12 09:26:58,334 INFO sqlalchemy.engine.base.Engine (1,)
>>> user
User:wang
>>> db.session.delete(user) 
>>> db.session.commit()
2019-08-12 09:27:27,209 INFO sqlalchemy.engine.base.Engine DELETE FROM users WHERE users.id = %s
2019-08-12 09:27:27,209 INFO sqlalchemy.engine.base.Engine (1,)
2019-08-12 09:27:27,225 INFO sqlalchemy.engine.base.Engine COMMIT
>>> User.query.all()
2019-08-12 09:27:36,097 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-12 09:27:36,098 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users
2019-08-12 09:27:36,098 INFO sqlalchemy.engine.base.Engine ()
[User:zhang, User:chen, User:zhou]

更新数据:

>>> user = User.query.first()
2019-08-12 09:28:09,206 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
 LIMIT %s
2019-08-12 09:28:09,206 INFO sqlalchemy.engine.base.Engine (1,)
>>> user 
User:zhang
>>> user.name
'zhang'
>>> user.name = "furuiyang"
>>> db.session.commit()
2019-08-12 09:28:34,955 INFO sqlalchemy.engine.base.Engine UPDATE users SET name=%s WHERE users.id = %s
2019-08-12 09:28:34,955 INFO sqlalchemy.engine.base.Engine ('furuiyang', 2)
2019-08-12 09:28:34,960 INFO sqlalchemy.engine.base.Engine COMMIT
>>> User.query.first()
2019-08-12 09:28:46,551 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-08-12 09:28:46,552 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
 LIMIT %s

2019-08-12 09:28:46,552 INFO sqlalchemy.engine.base.Engine (1,) User:furuiyang

使用 update 更新数据:

>>> User.query.filter_by(name='furuiyang').update({"name": "Katrina"})
2019-08-12 09:30:38,053 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.id = %s
2019-08-12 09:30:38,054 INFO sqlalchemy.engine.base.Engine (3,)
2019-08-12 09:30:38,058 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE users.id = %s
2019-08-12 09:30:38,059 INFO sqlalchemy.engine.base.Engine (4,)
2019-08-12 09:30:38,060 INFO sqlalchemy.engine.base.Engine UPDATE users SET name=%s WHERE users.name = %s
2019-08-12 09:30:38,060 INFO sqlalchemy.engine.base.Engine ('Katrina', 'furuiyang')
1
>>> User.query.all()
2019-08-12 09:31:00,951 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users
2019-08-12 09:31:00,951 INFO sqlalchemy.engine.base.Engine ()
[User:Katrina, User:chen, User:zhou]

关联查询示例: 角色和用户是一对多的关系。一个角色可以有多个用户,但是一个用户只能属于一个角色:

查询某个角色组(ruiyang) 下的全部用户:

>>> role_ruiyang = Role.query.filter_by(name='ruiyang').first() 
2019-08-12 09:35:24,302 INFO sqlalchemy.engine.base.Engine SELECT roles.id AS roles_id, roles.name AS roles_name 
FROM roles 
WHERE roles.name = %s 
 LIMIT %s
2019-08-12 09:35:24,302 INFO sqlalchemy.engine.base.Engine ('ruiyang', 1)
>>> role_ruiyang 
Role:ruiyang
>>> role_ruiyang.us
2019-08-12 09:35:41,776 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.email AS users_email, users.pswd AS users_pswd, users.role_id AS users_role_id 
FROM users 
WHERE %s = users.role_id
2019-08-12 09:35:41,776 INFO sqlalchemy.engine.base.Engine (3,)
[User:zhou]

查询某个用户所属的角色:

>>> user = User.query.get(3) 
>>> user
User:chen
>>> user.role
Role:user1

数据库迁移之 Flask-Migrate

安装

pip install flask-migrate

创建一个测试 app

如下:定义了两个模型类,作者和书名:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:ruiyang@127.0.0.1:3306/test'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)


class Author(db.Model):
    """
    定义模型类-作者
    """
    __tablename__ = 'author'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    email = db.Column(db.String(64))
    au_book = db.relationship('Book', backref='author')

    def __str__(self):
        return f'Author:{self.name}'


class Book(db.Model):
    """
    定义模型类-书名
    """
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    info = db.Column(db.String(32), unique=True)
    leader = db.Column(db.String(32))
    au_book = db.Column(db.Integer, db.ForeignKey('author.id'))

    def __str__(self):
        return f'Book:{self.info},{self.lead}'


if __name__ == "__main__":
    app.run()

开启 flask shell 创建表:

(ihome)  furuiyang@furuiyangdeMacBook-Pro  ~/codes/iHomeDemo   master  flask shell 
Python 3.7.3 (default, Jun 19 2019, 07:38:49) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
App: app [production]
Instance: /Users/furuiyang/codes/iHomeDemo/instance
>>> from app import db 
>>> db.create_all()

在数据库中查看创建出的表结构:

mysql> desc author;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| name  | varchar(32) | YES  | UNI | NULL    |                |
| email | varchar(64) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> desc books;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type        | Null | Key | Default | Extra          |
+---------+-------------+------+-----+---------+----------------+
| id      | int(11)     | NO   | PRI | NULL    | auto_increment |
| info    | varchar(32) | YES  | UNI | NULL    |                |
| leader  | varchar(32) | YES  |     | NULL    |                |
| au_book | int(11)     | YES  | MUL | NULL    |                |
+---------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

生成一些测试数据:

>>> au1 = Author(name='八月长安',email='changan@163.com')
>>> au2 = Author(name='余秋雨',email='qiuyu@126.com')
>>> au3 = Author(name='冯唐',email='tang@163.com')

>>> bk1 = Book(info='最好的我们',leader='耿耿&余淮')
>>> bk2 = Book(info='文化苦旅',leader='风景抒情')
>>> bk3 = Book(info='万物生长',leader='秋水')
>>> db.session.add_all([au1, au2, au3, bk1, bk2, bk3])
>>> db.session.commit()

查看数据库:

mysql> select * from author;
+----+--------------+-----------------+
| id | name         | email           |
+----+--------------+-----------------+
|  1 | 八月长安     | changan@163.com |
|  2 | 余秋雨       | qiuyu@126.com   |
|  3 | 冯唐         | tang@163.com    |
+----+--------------+-----------------+
3 rows in set (0.00 sec)

mysql> select * from books;
+----+-----------------+---------------+---------+
| id | info            | leader        | au_book |
+----+-----------------+---------------+---------+
|  1 | 最好的我们      | 耿耿&余淮     |    NULL |
|  2 | 文化苦旅        | 风景抒情      |    NULL |
|  3 | 万物生长        | 秋水          |    NULL |
+----+-----------------+---------------+---------+
3 rows in set (0.00 sec)

添加一个模板展示页面:

<h1>书籍展示</h1>
    <form method="post">
        {{ form.csrf_token }}
        <p>作者:{{ form.au_info }}</p>
        <p>书名:{{ form.bk_info }}</p>
        <p>{{ form.submit }}</p>
    </form>
    <ul>
        <li>{% for x in author %}</li>
        <li>{{ x }}</li><a href='/delete_author{{ x.id }}'>删除</a>
        <li>{% endfor %}</li>
    </ul>
    <hr>
    <ul>
        <li>{% for x in book %}</li>
        <li>{{ x }}</li><a href='/delete_book{{ x.id }}'>删除</a>
        <li>{% endfor %}</li>
    </ul>

添加视图路由:

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:ruiyang@127.0.0.1:3306/test'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config['SECRET_KEY'] = 'ruiyang'

db = SQLAlchemy(app)


class Author(db.Model):
    """
    定义模型类-作者
    """
    __tablename__ = 'author'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    email = db.Column(db.String(64))
    au_book = db.relationship('Book', backref='author')

    def __str__(self):
        return f'Author:{self.name}'


class Book(db.Model):
    """
    定义模型类-书名
    """
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    info = db.Column(db.String(32), unique=True)
    leader = db.Column(db.String(32))
    au_book = db.Column(db.Integer, db.ForeignKey('author.id'))

    def __str__(self):
        return f'Book:{self.info},{self.leader}'


class Append(Form):
    """创建表单类,用来添加信息"""
    au_info = StringField(validators=[DataRequired()])
    bk_info = StringField(validators=[DataRequired()])
    submit = SubmitField(u'添加')


@app.route('/', methods=['GET', 'POST'])
def index():
    # 查询所有作者和书名信息
    author = Author.query.all()
    book = Book.query.all()
    # 创建表单对象
    form = Append()
    if form.validate_on_submit():
        # 获取表单输入数据
        wtf_au = form.au_info.data
        wtf_bk = form.bk_info.data
        # 把表单数据存入模型类
        db_au = Author(name=wtf_au)
        db_bk = Book(info=wtf_bk)
        # 提交会话
        db.session.add_all([db_au,db_bk])
        db.session.commit()
        # 添加数据后,再次查询所有作者和书名信息
        author = Author.query.all()
        book = Book.query.all()
        return render_template('index.html', author=author, book=book, form=form)
    else:
        if request.method == 'GET':
            render_template('index.html', author=author, book=book, form=form)
    return render_template('index.html', author=author, book=book, form=form)


@app.route('/delete_author<id>')
def delete_author(id):
    """删除作者"""
    # 精确查询需要删除的作者id
    au = Author.query.filter_by(id=id).first()
    db.session.delete(au)
    # 直接重定向到index视图函数
    return redirect(url_for('index'))


@app.route('/delete_book<id>')
def delete_book(id):
    """删除书名"""
    # 精确查询需要删除的书名id
    bk = Book.query.filter_by(id=id).first()
    db.session.delete(bk)
    # 直接重定向到index视图函数
    return redirect(url_for('index'))


if __name__ == "__main__":
    app.run(debug=True)

在 app 中加入扩展

创建迁移数据库

(ihome)  ✘ furuiyang@furuiyangdeMacBook-Pro  ~/codes/iHomeDemo   master ●✚  flask db init 
  Creating directory /Users/furuiyang/codes/iHomeDemo/migrations ... done
  Creating directory /Users/furuiyang/codes/iHomeDemo/migrations/versions ... done
  Generating /Users/furuiyang/codes/iHomeDemo/migrations/script.py.mako ... done
  Generating /Users/furuiyang/codes/iHomeDemo/migrations/env.py ... done
  Generating /Users/furuiyang/codes/iHomeDemo/migrations/README ... done
  Generating /Users/furuiyang/codes/iHomeDemo/migrations/alembic.ini ... done

创建迁移数据脚本

(ihome)  furuiyang@furuiyangdeMacBook-Pro  ~/codes/iHomeDemo   master ●✚  flask db migrate -m 'first' 
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'author'
INFO  [alembic.autogenerate.compare] Detected added table 'books'
  Generating /Users/furuiyang/codes/iHomeDemo/migrations/versions/947c0bffbb1b_first.py ... done

使用迁移脚本更新数据库

(ihome)  furuiyang@furuiyangdeMacBook-Pro  ~/codes/iHomeDemo   master ●✚  flask db upgrade 
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 947c0bffbb1b, first

数据库变动

查看历史版本

flask db history 

版本回退

flask db downgrade 版本号

注册shell上下文

在我们启动 flask shell的时候,我们就实现了在上下文中启动了一个python 解释器。这意味着我们可以有了当前的应用实例:

使用常规的解释器会话时,除非明确地被导入,否则app对象是未知的,但是当使用flask shell时,该命令预先导入应用实例。 flask shell的绝妙之处不在于它预先导入了app,而是你可以配置一个“shell上下文”,也就是可以预先导入一份对象列表。

在入口脚本中实现一个函数,通过添加数据库实例和模型来创建了一个shell上下文环境:

from app import app, db
from app.models import User, Post

@app.shell_context_processor
def make_shell_context():
    return {'db': db, 'User': User, 'Post': Post}

app.shell_context_processor装饰器将该函数注册为一个shell上下文函数。 当flask shell命令运行时,它会调用这个函数并在shell会话中注册它返回的项目。 函数返回一个字典而不是一个列表,原因是对于每个项目,你必须通过字典的键提供一个名称以便在shell中被调用。