Flask框架 完整实战案例 附代码解读 【3】
Flask 是一个轻量级的可定制框架,使用Python语言编写,较其他同类型框架更为灵活、轻便、安全且容易上手。
前面已经写过项目从新建运行安装到测试部署的全流程,其中有写Flask框架从新建到部署全流程,但是只有部分代码。本篇主要是实战案例项目的代码运行全流程说明,自己总结的方便查看,也欢迎正在学习的宝子参考,如果有不对的地方,请评论区留言或者私信我,感谢。
目录
一、新建项目以及虚拟环境
二、项目案例代码部分
1.应用工厂
1.1 运行应用
2.数据库
2.1 与数据库建立连接
2.2 创建数据表
2.3 在应用中注册
2.4 初始化数据库文件
3.蓝图和视图
3.1创建蓝图
3.2新建视图 注册用户
4.模板
4.1基础布局
4.2 注册模板
4.3 注册一个用户
一、新建项目以及虚拟环境
这里把新建项目以及虚拟环境在写一遍:
这里可以用命令行新建、也可以直接右键新建项目。然后命令行打开文件夹,这里只是举例子,本次项目的项目名称是 flask-tutorial 文件夹。
py -3 -m venv venv # 创建虚拟环境
venv\Scripts\activate # 激活虚拟环境
新建的项目要新建虚拟环境然后激活虚拟环境,对于已经新建过虚拟环境的直接激活虚拟环境就可以了。
后面下载框架或者项目中需要的包运行应用都是在虚拟环境下进行的。
二、项目案例代码部分
一个 Flask应用是一个Flask 类的实例,我们在代码的最开始创建一个全局 Flask 实例。如何创建全局实例呢?可以在一个函数内部创建一个flask实例,然后把这个应用返回,这个函数被称为应用工厂,因为所有应用的相关配置、注册登录和其他设置都是在这一个函数完成的。
1.应用工厂
创建一个 flaskr 文件夹,然后新建 __init__.py文件。应用里面的 SECRET_KEY 变量也就是我们上一篇关于项目打包部署的时候生成密钥的值。
SECRET_KEY 是被 Flask 和扩展用于保证数据安全的。在开发过程中, 为了方便可以设置为 'dev'
,但是在发布的时候应当使用一个随机值来 重载它。
# flaskr/__init__.py 文件代码部分
import os
from flask import Flask
def create_app(test_config=None):
# 创建一个应用实例app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# a simple page that says hello
@app.route('/hello')
def hello():
return 'Hello, World!'
return app #返回应用
1.1 运行应用
前面我们已经设置了应用工厂,所以这里我们应用模块就是flaskr,在设置开发模式,然后运行应用。事实上,后面我们在加入其他代码包括设置或者功能模块也是这样运行的,因为都是在工厂模式里面调用的。
set FLASK_APP=flaskr #设置应用在哪
> set FLASK_ENV=development #设置开发模式
> flask run #运行应用
2.数据库
Python 内置了 SQLite 数据库支持,项目也是使SQLite来存储数据的,相应的模块为sqlite3.
使用 SQLite 的便利性在于不需要单独配置一个数据库服务器,并且 Python 提供了 内置支持。小应用没有问题,但是大应用可能就需要考虑换成别的数据库了。
2.1 与数据库建立连接
新建flaskr/db.py文件:
# flaskr/db.py 文件代码
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext
def get_db():
if 'db' not in g:
#建立一个数据库连接 该连接指向配置中的 DATABASE 指定的文件
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
#告诉连接返回类似于字典的行,这样可以通过列名称来操作 数据
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop('db', None)
# 如果连接已建立就关闭连接
if db is not None:
db.close()
在上面的代码中 g 是一个特殊对象,独立于每一个请求,用于存储请求过程中可能多个函数都会用到的数据,把连接储存起来可以多次使用。
current_app 是另一个特殊对象,该对象指向处理请求的 Flask 应用,就是使用这个特殊对象,get_db
函数才会被调用。
2.2 创建数据表
现在要创建一个sql文件,用于生成应用中用到的数据表的sql语句,我们用数据表user来存储用户信息,用数据表名posts来存储博客数据。
新建文件flaskr/schema.sql :
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);
sql文件写好了,我们需要调用这个文件执行sql语句,前面我们已经db.py文件中已经连接数据,我们继续在db.py文件中写入python函数用来执行sql文件:
flaskr/db.py文件中新增执行sql文件的函数:
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f: #打开sql文件
db.executescript(f.read().decode('utf8')) #执行sql文件命令
# #定义一个名为 init-db 命令行,它调用 init_db 函数,并为用户显示一个成功的消息
@click.command('init-db')
@with_appcontext
def init_db_command():
"""Clear the existing data and create new tables."""
init_db()
click.echo('Initialized the database.')
2.3 在应用中注册
close_db
和 init_db_command
函数需要在应用实例中注册,否则无法使用,我们写一个函数,把应用作为参数,在函数中进行注册。
flaskr/db.py新增代码:
def init_app(app):
app.teardown_appcontext(close_db) #告诉 Flask 在返回响应后进行清理的时候调用此函数
app.cli.add_command(init_db_command) #添加一个新的 可以与 flask 一起工作的命令。
然后在工厂中调用init_app()函数.在工厂中导入并调用这个函数。在工厂函数中把新的代码放到 函数的尾部,返回应用代码的前面。
flaskr/__init__.py文件代码新增:
def create_app():
app = ...
# existing code omitted
from . import db
db.init_app(app)
return app
2.4 初始化数据库文件
我们现在已经把应用在函数中进行注册了,现在可以运行应用了,还是和上面一样设置下应用模块然后flask run 运行:
set FLASK_APP=flaskr #设置应用在哪
set FLASK_ENV=development #设置开发模式
flask run #运行应用
flask init-db
#Initialized the database.
现在会有一个 flaskr.sqlite
文件出现在项目所在文件夹的 instance
文件夹 中。
3.蓝图和视图
试图和蓝图不一样,简单的讲视图是响应请求的函数,返回数据。Blueprint蓝图是组织一组相关视图和其他代码的方式。把视图和代码注册到蓝图,然后在工厂函数中 把蓝图注册到应用。
3.1创建蓝图
Flaskr 有两个蓝图,一个用于认证功能,另一个用于博客帖子管理。每个蓝图的代码 都在一个单独的模块中。使用博客首先需要认证,因此我们先写认证蓝图。
新建文件flaskr/auth.py 代码如下:
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from flaskr.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
这里创建了一个名称为 'auth'
的 Blueprint 。和应用对象一样, 蓝图需要知道是在哪里定义的,因此把 __name__
作为函数的第二个参数。 url_prefix
会添加到所有与该蓝图关联的 URL 前面。
使用 app.register_blueprint 导入并注册 蓝图。新的代码放在工厂函数的尾部返回应用之前。
flaskr/__init__.py文件新增代码:
def create_app():
app = ...
# existing code omitted
from . import auth
app.register_blueprint(auth.bp)
return app
认证蓝图将包括注册新用户、登录和注销视图。
3.2新建视图 注册用户
在flaskr/auth.py文件中新增视图代码:
#bp.route关联了 URL /register 和 register 视图函数
@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
elif db.execute(
'SELECT id FROM user WHERE username = ?', (username,)
).fetchone() is not None:
error = 'User {} is already registered.'.format(username)
if error is None:
# db.execute使用了带有 ? 占位符 的 SQL 查询语
db.execute(
'INSERT INTO user (username, password) VALUES (?, ?)',
(username, generate_password_hash(password))
)
db.commit()
return redirect(url_for('auth.login'))
flash(error)
return render_template('auth/register.html')
占位符可以代替后面的元组参数中相应的值。使用占位符的 好处是会自动帮你转义输入值,以抵御 SQL 注入攻击 。
fetchone() 根据查询返回一个记录行。 如果查询没有结果,则返回 None
。视图中如果查询到用户已存在则提示用户已存在,如果用户不存在就向数据插入一条用户信息的数据,然后提交数据库,跳转到登录页面。当用户最初访问视图会打开一个注册页面的表单,render_template会渲染一个包含 HTML 的模板。
后面登录、注销其实逻辑都是一样的。
4.模板
模板文件其实就是HTML。会储存在 flaskr
包内的 templates
文件夹内。模板是包含静态数据和动态数据占位符的文件。模板使用指定的数据生成最终的文档。Flask 使用 Jinja 模板库来渲染模板。
4.1基础布局
应用中的每一个页面主体不同,但是基本布局是相同的。每个模板会 扩展 同一个 基础模板并重载相应的小节,而不是重写整个 HTML 结构。
新建flaskr/templates/base.html文件 :
<!doctype html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
<h1>Flaskr</h1>
<ul>
{% if g.user %}
<li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('auth.register') }}">Register</a>
<li><a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}
</ul>
</nav>
<section class="content">
<header>
{% block header %}{% endblock %}
</header>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</section>
在模板中 g 和 url_for() 可直接使用,g.user()显示用户状态,用户名或者注销显示注册和登录连接,url_for可用于生成视图的 URL ,而不用手动来指定。
模板中定义三个块,这些块会被其他模板重载。
-
{% block title %}
会改变显示在浏览器标签和窗口中的标题。 -
{% block header %}
类似于title
,但是会改变页面的标题。 -
{% block content %}
是每个页面的具体内容,如登录表单或者博客帖子。
其他模板直接放在 templates
文件夹内。为了更好地管理文件,属于某个蓝图 的模板会被放在与蓝图同名的文件夹内。
4.2 注册模板
flaskr/templates/auth/register.html
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Register">
</form>
{% endblock %}
{% extends 'base.html' %}
告诉 Jinja 这个模板基于基础模板,并且需要替换 相应的块。所有替换的内容必须位于 {% block %}
标签之内。
一个实用的模式是把 {% block title %}
放在 {% block header %}
内部。 这里不但可以设置 title
块,还可以把其值作为 header
块的内容, 一举两得。
静态文件 除了 CSS ,其他类型的静态文件可以是 JavaScript 函数文件或者 logo 图片。它们 都放置于
flaskr/static
文件夹中,并使用url_for('static', filename='...')
引用。基础模板中有用到引入样式文件。
4.3 注册一个用户
现在浏览器输入http://127.0.0.1:5000/auth/register 可以注册一个用户了。请确定服务器还在运行
博客蓝图和视图逻辑都类似的。
项目代码部分完成后,然后项目可安装化和部署前面一篇项目部署全流程都总结过啦!