一、Flask基础
1.Flask环境搭建
- Python解释器
- 安装Flask
- 安装PyCharm
2.Flask项目创建
from flask import Flask
# 创建应用实例
app = Flask(__name__)
@app.route('/zs') # 路由
# 视图函数
def hello_zs():
return 'Hello 张三!'
@app.route('/') # 路由
# 视图函数
def hello_world():
return 'Hello Teacher!'
if __name__ == '__main__':
# host:主机
# port:端口
app.run() # 运行应用
注意:
1.如何修改远程访问(IP)?
点击“Edit Configurations…”打开配置窗口,在“Additional Options”中输入“–host=0.0.0.0 --port=8000”
2.如何开启Debug模式?
点击“Edit Configurations…”打开配置窗口,勾选“FLASK_DEBUG”复选框。
二、Web前端基础
2.1 HTML
1.基本语法:
(1)标签 <xxx></xxx>
(2)属性 <xxx attr1=value1 attr2=value2></xxx>
(3)内容 <xxx>内容</xxx>
2.网页标题
<title>
3.文本
(1)<h1~h6>
(2)<p>
(3)<a>
(4)<ul>
(5)<ol>
(6)<li>
(7)<span>
4.图像
(1)<img src='' alt='' width='' height=''>
5.表格
(1)<table>
(2)<tr>
(3)<td>
(4)<th>
6.表单
(1)<form name='' action='' method='get/post'></form>
(2)<input name="表单元素名称" type="类型" value="值" size="显示宽度" maxlength="能输入的最大字符长度" checked="是否选中"/>
2.2 CSS
1.三种使用方式
(1)行内样式
(2)内嵌样式
(3)外部样式
2.3 JavaScript
1.三种使用方式
(1)行内脚本
(2)内嵌脚本
(3)外部脚本
2.4 BootStrap
1.下载安装
2.第一个BootStrap页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 移动设备优先-->
<!-- 为了让 Bootstrap 开发的网站对移动设备友好,确保适当的绘制和触屏缩放-->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BootStrap02</title>
<!-- 新 Bootstrap5 核心 CSS 文件 -->
<link rel="stylesheet" href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/twitter-bootstrap/5.1.3/css/bootstrap.min.css">
<!-- popper.min.js 用于弹窗、提示、下拉菜单 -->
<script src="https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/popper.js/2.11.2/umd/popper.min.js"></script>
<!-- 最新的 Bootstrap5 核心 JavaScript 文件 -->
<script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/twitter-bootstrap/5.1.3/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-3 bg-info">.col-md-3</div>
<div class="col-md-3 bg-danger">.col-md-3</div>
<div class="col-md-3 bg-info">.col-md-3</div>
<div class="col-md-3 bg-danger">.col-md-3</div>
</div>
</div>
</body>
</html>
3.网格系统
三、路由
3.1 注册路由
注册路由:就是在Flask程序建立URL与视图函数或类的映射关系的过程。
注册路由的语法:
@app.route(rule, methods, **options)
rule:必选参数,表示URL规则字符串,该字符串必须以“/”开始。
methods:可选参数,表示HTTP请求方法。
**options:可选参数,表示传递给底层Rule对象的额外选项。
注意事项:
- 若URL规则字符串以“/”结尾,但用户访问URL时并没有在URL末尾附加“/”,则会自动重定向到附加了“/”的页面;
- 若URL规则字符串的末尾没有附加“/”,但用户通过URL访问页面时在URL末尾附加了“/”,则会出现404页面。
3.2 传递参数
当调用route()方法注册路由时,可以在URL规则字符串中加入尖括号包裹的变量,用于标记URL中动态变化部分的内容,之后将该变量作为参数传递给视图函数。
若需要传递多个参数,可使用“/”隔开。
传递参数的语法:
@app.route("/<变量名>")
示例代码:
from flask import Flask
app = Flask(__name__)
@app.route('/<page>')
def page_num(page):
return f'当前为第{page}页'
if __name__ == '__main__':
app.run()
参数类型转换/指定参数类型:
当URL向视图函数传递参数时,参数类型默认是字符串类型,如果需要限定参数的类型,那么可以通过转换器指定参数的类型。
转换器支持两种类型,分别是内置转换器和自定义转换器。
@app.route("/<转换器:变量名>")
Flask框架中提供了6种内置转换器,如果为参数明确指定了转换器,那么URL中传递的参数必须符合转换器要求的数据类型。
示例代码:
from flask import Flask
app = Flask(__name__)
@app.route('/<int:page>')
def page_num(page):
return f'当前为第{page}页'
if __name__ == '__main__':
app.run()
若传入的参数值不能转换为整型,URL不会被匹配成功,会出现Not Found错误。
示例代码:
from flask import Flask
app = Flask(__name__)
@app.route('/<any(male,female):gender>')
def get_gender(gender):
return f'当前性别为{gender}'
if __name__ == '__main__':
app.run()
若传入的参数值不是male或female指定值,URL不会被匹配成功,会出现Not Found错误。
3.3 请求方式
在HTTP协议中,请求URL有不同的方法,不同的请求方法有不同的应用场景,下面先来了解有哪些HTTP请求方法以及使用方法:
HTTP 请求可以使用多种请求方式,常用方式包括以下两种:
- GET 请求方式(超链接、表单默认都是get请求)
- POST 请求方式。
GET和POST请求区别如下:
- GET只能传递文本,POST可传递任意数据(包括音频、视频等,上传文件必须POST)
- GET 请求方式提交的数据会暴露在地址栏,不安全,POST更加安全
- GET 请求方式提交的数据不能超过 2KB,POST 请求方式无此限制
- GET请求效率高于POST
设置请求方法:
在Flask项目中route默认用的是GET请求,如果想更改URL的请求方法,可以设置methods参数。
通过给methods参数赋值一个列表,列表中可以设置一个或多个请求方法名称。
语法:
@app.route(rule, methods=['GET或POST'])
@app.route('/blog/add',methods=['GET'])
def blog_add():
if request.method == 'GET':
print('使用GET方法添加博客')
elif request.method == 'POST':
print('使用POST方法添加博客')
return '添加博客'
快捷函数
在Flask2.0及其之后的版本提供了指定部分请求方式的便捷函数,这些函数与请求方法的名称相同,且都是小写形式的。需要通过装饰器的形式添加到在视图函数上方即可。
- get():route()传递methods=[“GET”]的快捷函数。
- post():route()传递methods=[“POST”]的快捷函数。
- put():route()传递methods=[“PUT”]的快捷函数。
- delete():route()传递methods=[“DELETE”]的快捷函数。
3.4 反向解析
语法:
url_for(endpoint, values)
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/hello/flask')
def greet():
return f"{url_for('greet')}"
if __name__ == '__main__':
app.run()
flask.url_for模块中提供了URL反向解析的函数url_for(),该函数可以根据视图函数的名称获取对应的URL。
若URL规则中包含要传递的参数,则调用url_for()函数时需要将该参数以关键字参数形式传递,还可以传递任何额外参数给URL地址的参数。
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/hello/<name>')
def greet(name):
# 将age=20添加到URL地址中
return f"{url_for('greet',name=name, age=20)}"
if __name__ == '__main__':
app.run()
四、请求和响应
1.request对象
在通过浏览器访问网页时,浏览器提交的请求中不仅包含URL,还包含其他数据,例如用户使用的系统与浏览器版本、语言、浏览器所支持的编码、格式等。
request中包含了前端发送过来的所有数据 ,请求的 request 对象中保存了一次HTTP请求的一切信息。
request对象中提供了用于处理请求信息的常用属性。
以下是针对 Flask 中 request
对象不同场景的示例代码,覆盖 JSON、表单、URL参数、请求方法和文件上传:
1. 处理 JSON 请求体
使用 request.get_json()
或 request.data
(推荐 request.get_json()
自动解析 JSON)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/json', methods=['POST'])
def handle_json():
# 方式1:直接获取原始数据(字符串)
raw_data = request.data
print("原始请求体:", raw_data.decode('utf-8'))
# 方式2:自动解析为字典(更常用)
json_data = request.get_json()
return jsonify({
"status": "success",
"data_received": json_data
})
测试方法:
curl -X POST -H "Content-Type: application/json" -d "{\"name\": \"Alice\", \"age\": 25}" http://localhost:5000/api/json
2. 处理表单数据
使用 request.form
获取表单参数
@app.route('/login', methods=['POST'])
def login():
username = request.form.get('username')
password = request.form.get('password')
return f"用户名: {username}, 密码: {password}"
测试方法:
curl -X POST -d "username=admin&password=123456" http://localhost:5000/login
3. 处理 URL 参数
使用 request.args
获取 URL 中的参数
@app.route('/search')
def search():
keyword = request.args.get('keyword', '') # 默认空字符串
page = request.args.get('page', 1, type=int) # 强制类型为整数
return f"搜索关键字: {keyword}, 第 {page} 页"
测试 URL:
http://localhost:5000/search?keyword=flask&page=2
4. 区分 GET/POST 请求方法
使用 request.method
判断请求类型
@app.route('/message', methods=['GET', 'POST'])
def message():
if request.method == 'GET':
return '''
<form method="POST">
<input type="text" name="msg">
<button type="submit">提交</button>
</form>
'''
elif request.method == 'POST':
msg = request.form.get('msg')
return f"你提交的消息是: {msg}"
测试方法:
- 浏览器访问
http://localhost:5000/message
提交表单
5. 处理文件上传
使用 request.files
获取上传的文件
import os
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
os.makedirs(UPLOAD_FOLDER, exist_ok=True) # 确保目录存在
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return "未选择文件", 400
file = request.files['file']
if file.filename == '':
return "文件名无效", 400
# 保存文件到服务器
file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename))
return f"文件 {file.filename} 上传成功"
测试方法:
curl -X POST -F "file=@/path/to/your/file.txt" http://localhost:5000/upload
关键总结
场景 | 属性/方法 | 典型用途 |
---|---|---|
JSON 数据 | request.get_json() | 解析 API 请求中的 JSON 数据 |
表单数据 | request.form | 处理 HTML 表单提交 |
URL 参数 | request.args | 获取 ?key=value 查询参数 |
请求方法 | request.method | 判断 GET/POST/PUT/DELETE 等 |
文件上传 | request.files | 接收用户上传的文件 |
扩展提示
- 参数校验:使用
request.form.get('key', default)
或request.args.get('key', default)
避免KeyError
- 错误处理:结合
try...except
处理数据类型转换错误(如将字符串转为整数) - 安全建议:
- 文件上传时检查文件扩展名和内容类型
- 限制上传文件大小(通过
app.config['MAX_CONTENT_LENGTH']
) - 对表单和 URL 参数做防 SQL 注入/XSS 攻击处理
2.响应
在Flask程序中,浏览器发出的请求会触发相应的视图函数,并将视图函数的返回值作为响应体,之后生成完整的响应内容,即响应报文。响应报文主要由4个部分组成,分别是状态行、响应报头、空行以及响应体。
状态码
Flask内部自动会将字符串转换成Response类的对象。在Flask中,Response类表示响应,它封装了响应报文的相关信息。
如果希望在Flask程序中主动生成响应,一般可以通过make_response()函数实现。
生成响应
make_response()函数用于生成响应,它可以接收str、bytes、dict和tuple共4种类型的参数,当参数的类型为tuple时,参数的值可以为(body, status, headers) 、(body, status)或 (body, headers)任意一种形式,其中body表示响应体,status表示状态码,headers表示响应报头,另外header的值可以是一个字典或(key,value)形式的元组。
make_response(body,status,headers)
make_response()函数生成响应-示例代码
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/index')
def index():
res = make_response('Python&Flask',201,
{'content-type':' text/html;charset=utf-8'})
return res
if __name__ == '__main__':
app.run()
JSON格式
若视图函数返回的响应体为JSON格式的字符串,可以通过json模块将Python字典、列表或元组序列化为JSON格式的字符串。
也可以通过Flask提供的便捷函数jsonify(),jsonify()函数会将传入的参数序列化为JSON格式的字符串。
两者的区别在于,前者会将响应类型设置为text/html,而后者会将响应类型设置为application/json。
生成JSON类型的响应数据
from flask import Flask, make_response, jsonify
app = Flask(__name__)
@app.route('/response')
def resp():
res = make_response(jsonify({'Python':'Flask'}),202)
return res
if __name__ == '__main__':
app.run()
@app.route('/manual_json')
def manual_json():
data = {
"status": "success",
"message": "手动序列化的 JSON",
"data": [1, 2, 3]
}
# 方式1:直接返回字符串(默认 Content-Type 为 text/html)
# return json.dumps(data)
# 方式2:手动设置 Content-Type 为 application/json
response = make_response(json.dumps(data))
response.headers['Content-Type'] = 'application/json'
return response
@app.route('/jsonify')
def use_jsonify():
data = {
"status": "success",
"message": "使用 jsonify 生成的 JSON",
"data": {"key": "value"}
}
return jsonify(data) # 自动设置 Content-Type 为 application/json
页面重定向
页面重定向在Web程序中非常普遍,例如,当用户在电商网站购买商品时,电商网站若检测到用户还未曾登录,则会将当前页面重定向到登录页面。在Flask程序中,页面重定向功能可以通过redirect()函数实现。
location:必选参数,表示重定向的URL地址。
redirect(location)
重定向到登录页面
from flask import Flask, url_for, request, redirect, session
app = Flask(__name__)
@app.route('/index')
def index():
# 页面重定向到登录页面
return redirect(url_for("login"))
3.cookie
4.session
五、模版基础
1.模版简介
Flask程序的模板是一个文件,它可以生成任何基于文本格式的文件,比如HMTL、XML、CSV等,模板文件的文本格式通常为HTML格式。模板文件中除了包含固定的HTML代码外,还可以包含描述数据插入HTML代码的动态内容,这些动态内容往往会按照模板引擎支持的特殊语法来标记为变量。
模板引擎用于识别模板中的特殊语法标记,它会结合请求上下文环境将变量替换为真实的值,生成最终的HTML页面,这个过程称为渲染。
渲染模版语法:
render_template(template_name_or_list, **context)
- 用render_template()函数使得Jinja2模板引擎渲染模板。
- template_name_or_list:必选参数,表示要加载的模板名称。
- **context:可选参数,表示向模板文件传递的参数,以关键字参数的形式进行传递。关键字参数的名称必须与模板文件中的变量名称保持一致。
2.模版语法
模板变量是一种特殊的占位符,它用于标识模板中会动态变化的数据,当模板被渲染时,模板引擎将模板中的变量替换为由视图函数传递的真实数据。
变量名称应与视图函数中传递的关键字参数的名称相同。模板变量的命名规则与Python变量的命名规则相同。
{{ 变量名 }}
Jinja2能够识别所有类型的变量,例如,列表、字典和对象等。
若变量保存的数据是列表,则可以通过索引获取列表中的某个元素;
若变量保存的数据是字典,则可以通过字典的键获取相应的值;
若变量保存的数据是对象,则可以通过点字符访问对象中的属性或方法。
列表名[索引]
字典名[键名]
对象名.属性名
对象名.方法名
注释用于对某些代码做功能性的说明,从而增加程序的可读性:
{# 注释内容 #}
3.过滤器
在Jinja2中,过滤器是用于修改或过滤模板变量值的特殊函数,使用过滤器可以获取更精确的数据。
变量名称和过滤器之间使用竖线分隔。如果没有任何参数传给过滤器,则括号可以省略。一个模板变量可以使用多个过滤器,多个过滤器之间使用竖线分隔。
{{ 变量名|过滤器(参数) }}
4.控制结构
1.选择结构
{% if 条件语句1 %}
语句1
{% elif 条件语句2 %}
语句2
……
{% else %}
语句n
{% endif %}
2.循环结构
{% for 临时变量 in 变量 %}
语句
{% endfor %}
六、模版进阶
1.模版的包含与继承
模版包含
模板的包含允许您将一个模板嵌入到另一个模板中,从而实现模块化和代码重用。这对于在多个页面中使用相同的组件或块非常有用。
{% include '文件名' %}
模版继承
模板继承通过block和extends实现,其中block用于标识与继承机制相关的代码块,extends用于指定子模板所继承的父模板。
为了能够让子模板便于覆盖或插入内容到父模板中,需要在父模板使用block和endblock定义块代码,同时在子模板中通过定义同名的块。
{% block 块名称 %}
父模块代码
{% endblock %}
{% extends %}必须位于子模板的第一行,当模板引擎解析到{% extends %}时,会将父模板中的内容完整的复制到子模板中。
如果子模板实现了父模板中定义的block,那么子模板block中的代码就会覆盖父模板中的代码,如果想要在子模板中仍然呈现基模板中的内容,那么可以使用super()函数实现。
{% extends '父模板名称' %}
{% block 块名称 %}
子模块代码
{% endblock %}
2.宏的定义与调用
宏的定义
{% macro 宏的名称(参数列表)%}
宏内部逻辑代码
{% endmacro %}
宏的调用
宏的名称(参数列表)
宏的导入
{% import '宏文件的路径' [as 宏的别名] %}
{% from '宏文件的路径' import 宏的名字 [as 宏的别名] %}
3.注册全局对象
语法:
app.add_template_global(注册的函数名,'别名')
在 Jinja2 模板引擎中,有的时候可以通过函数调用来获取数据,但是在继承的模板里面,如果需要调用某个固定的函数,通常需要在每一个使用被继承模板的视图函数中注入相应的函数(变量),这时需要注册全局对象。
4.Flask-BootStrap
前面的内容中提到了Bootstrap前端框架,同时也通过手动引用、构建模板的方式,使用了Bootstrap框架。而在快速开发场景中,这种从零开始配置的方式并不适用。Flask-Bootstrap提供了一系列的基本模板,包含对资源的引用、一些快捷宏指令等。
安装:
pip install flask-bootstrap
初始化:
from flask import Flask, render_template
from flask_bootstrap import Bootstrap
app = Flask(__name__)
# flask_bootstrap 初始化代码
bootstrap = Bootstrap()
bootstrap.init_app(app)
# 使用本地资源,禁用cdn
bootstrap_cdns = app.extensions['bootstrap']['cdns']
bootstrap_cdns['bootstrap'] = bootstrap_cdns['local']
bootstrap_cdns['jquery'] = bootstrap_cdns['local']
Flask-Bootstrap提供了常用的基础模板,可节省编写基础模板的时间。
Flask-Bootstrap提供的基准模板(base.html)定义了一些常用区块,通常情况下,只要继承基准模板,并重写区块,便可以满足大部分的需求。基准模板中定义的一些常用区块,如下表所示
七、WTF表单基础
1.表单
网页的交互是指用户通过与网页上的元素(如按钮、表单、链接等)进行互动,以触发和响应网页的动态行为或功能。这种互动性是现代网页应用程序的关键组成部分,它使用户能够与网页进行实时通信、提交表单、加载新内容、执行操作等。
表单除了可以提交消息以外,还可以上传文件。在Flask中上传文件只需要定义两个视图函数即可:一个用于上传文件,另一个用于获取上传的文件。
2.Flask-WTF
Flask-WTF是Flask中专门用于处理表单的扩展包,有效地降低了表单维护的难度。
WTForms带有数据验证功能,便于服务器端对数据进行验证;同时还带有跨站请求伪造(Cross-Site Request Forgery,CSRF)保护功能,能有效防止用户受到黑客攻击。
安装依赖
pip install flask-wtf
在使用Flask-WTF表单之前,需要定义表单类。所有表单类都需要继承“FlaskForm”表单类,这是Flask-WTF对WTForms表单的封装。
表单类用于描述表单的结构,表单字段使用类变量进行描述。
语法:
from flask_wtf import FlaskForm
from wtforms import 表单字段类型
class 自定义表单类(FlaskForm):
3.渲染表单
渲染表单即根据表单类所描述的结构来生成相应HTML代码的过程。
方式一:
使用Flask-Bootstrap所提供的表单宏指令进行表单的快速生成。
{% import 'bootstrap/wtf.html' as wtf %}
{{ wtf.quick_form(form) }}
方式二:
还可以使用Flask-Bootstrap所提供的表单宏指令逐个字段生成表单。
{% import 'bootstrap/wtf.html' as wtf %}
<form role="form" method="post">
{{ wtf.form_field(form.字段名) }}
</form>
方式三:
每个表单字段单独渲染为原始样式,常用于建立复杂表单。
<form role="form" method="post">
{{ form.字段名 }}
</form>
4.处理表单
数据验证
接受表单数据
方式一:单个接收
语法:
form.xxx.data
示例代码:
def login():
form = LoginForm()
if request.method == 'POST' and form.validate_on_submit():
return 'Hello %s!<br>你所输入的密码为:%s' % (form.username.data, form.password.data)
else:
return render_template('login.html', form=form)
方式二:整体接收
语法:
form.data
示例代码:
def register():
form = RegisterForm()
if request.method == 'POST' and form.validate_on_submit():
# return 'Hello %s!您的性别为 %s' % (form.username.data,form.sex.data)
info = ''
for k,v in form.data.items():
info += '%s: %s <br>' % (k,v)
return info
else:
return render_template('register.html', form=form)
八、WTF表单进阶
消息闪现
消息反馈是指在 Web 应用中,通过文本、图标、颜色等方式向用户传达操作结果、状态变化或需要注意的信息。它用于向用户提供操作成功、操作失败、警告或其他相关信息,以帮助用户了解当前状态并做出适当的响应。
1、确认操作结果: 用户提交表单、执行操作或发起请求后,消息反馈可以告知用户操作是否已成功完成。这有助于用户确认他们的操作结果。
2、提醒警告: 某些操作可能涉及风险或需要特别注意。消息反馈可以警告用户可能遇到的问题或后果。
3、错误提示: 如果用户的操作存在错误,消息反馈可以清楚地说明错误的原因,帮助用户理解并纠正错误。
4、增强用户体验: 良好的消息反馈可以提升用户体验,让用户感受到应用程序的响应性和友好性。
5、避免混淆: 当用户执行某些需要时间的操作时,消息反馈可以避免用户的重复操作,因为他们会知道应用正在处理请求。
from flask import flash
flash('消息文字', 'success|danger|info|warning') #创建消息
get_flashed_messages(with_categories=true) #获取消息
消息渲染指令
{% import 'bootstrap/utils.html' as utils %}
{{ utils.flashed_messages() }}
自定义错误页
默认的错误页面,是一个纯文字的页面,如果网站带有自定义的风格设计,则无法与网站整体的风格相匹配。
在Flask中使用自定义错误页与绑定视图函数类似,但需要使用特殊的路由函数errorhandler()。
@app.errorhandler(错误代码)
**注意:**自定义500错误页面时,运行时如果不生效,需要关闭debug模式。
Flask-CKEditor
CKEditor是一个流行的富文本编辑器,可以在浏览器中创建和编辑富文本内容,如格式化文本、插入图像、创建表格等。
Flask-CKEditor是Flask框架中的一个扩展,它集成了CKEditor富文本编辑器到Flask应用中,针对Flask对CKEditor进行了适配,使其支持Flask-WTF,也可以通过"wtf"快速生成表单。
1.安装依赖
与安装Flask的操作一致,打开命令提示符窗口,输入以下命令。
pip install flask-ckeditor
2.基本使用
安装完成后,便可以在程序中使用CKEditor。其初始化代码与其他模块(如Flask-WTF、Flask-Bootstrap)的相似。
初始化完成,便是定义表单类、视图函数,以及相应的模板。
from flask_ckeditor import CKEditor
# 使用本地资源文件
app.config['CKEDITOR_SERVE_LOCAL'] = True
ckeditor = CKEditor()
# 初始化Flask-CKEditor
ckeditor.init_app(app)
示例代码:
# 编辑和查看文章模板表单类
from flask_ckeditor import CKEditorField
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
class ArticleForm(FlaskForm):
title = StringField(label='标题', validators=[DataRequired()])
content = CKEditorField(label='内容', validators=[DataRequired()])
submit = SubmitField(label='查看')
#视图函数
@app.route('/article', methods=['GET', 'POST'])
def article():
form = ArticleForm()
if request.method == 'POST' and form.validate_on_submit():
return render_template('ckview.html', form=form)
else:
return render_template('ckedit.html', form=form)
#文章编辑模板ckedit.html
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}CKEditor基本使用{% endblock %}
{% block content %}
<div class="container">{{ wtf.quick_form(form) }}</div>
{{ ckeditor.load() }}
{{ ckeditor.config(name='content', height=320) }}
{% endblock %}
#文章查看模板ckview.html
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}{{ form.title.data }}{% endblock %}
{% block content %}
<div class="container">
<h1>{{ form.title.data }}</h1>
<hr>
{{ form.content.data | safe }} #过滤器(关闭HTML转义)
</div>
{% endblock %}
资源上传
上一小节简单地演示了CKEditor的富文本编辑功能,但还不够全面。在常见的使用场景中,编辑内容的过程中需要插入图像,但前面的例子中,富文本编辑器仅支持添加在线图像,使用起来并不方便。为此,接下来需要实现图像上传功能。
实现资源上传功能需要为CKEditor指定文件上传所使用的视图函数及用于获取已上传的文件视图函数。
上传文件的视图函数需要使用Flask-CKEditor中所提供的API返回响应内容。
# 指定文件上传所使用的视图函数
app.config['CKEDITOR_FILE_UPLOADER'] = '视图函数'
# 上传成功后返回图像链接
upload_success(url=图像链接)
# 上传失败后返回错误提示
upload_fail(message='错误提示')
示例代码:
import os
from flask import send_from_directory, url_for
from flask_ckeditor import upload_fail, upload_success
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload_ckeditor'
@app.route('/upload_ckeditor', methods=['POST'])
def upload_ckeditor():
f = request.files.get('upload')
extension = f.filename.split('.')[1].lower()
if extension not in ['jpg', 'gif', 'png', 'jpeg', 'zip']:
return upload_fail(message='不支持的文件!')
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
return upload_success(url=url_for('uploaded_files', filename=f. filename))
获取上传文件的视图函数:
# 定义上传文件所存放的位置,此处定义为项目目录下的"uploads"目录
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['UPLOADED_PATH'] = os.path.join(basedir, 'uploads')
os.makedirs(app.config['UPLOADED_PATH'], exist_ok=True)
# 用于获取上传的文件
@app.route('/files/<filename>')
def uploaded_files(filename):
path = app.config['UPLOADED_PATH']
return send_from_directory(path, filename)
九、数据库操作
1.数据库概述
SQL
结构化查询语言(Structured Query Language,SQL)专门为了数据库查询而设计,用于存取数据,查询、更新和管理数据库。
数据库的作用
提供用户注册、文章发布等功能的网站都需要存储数据,在需要存储数据的情况下便需要使用数据库。
SQL语句
数据添加语句:INSERT 表名[(列名,…)] VALUES(值,…)
数据修改语句:UPDATE 表名 SET 列名=值,… [WHERE条件]
数据删除语句:DELETE FROM 表名 [WHERE条件]
数据查询语句:SELECT 列名,… FROM 表名 [WHERE条件]
使用原生SQL语句操作数据库,主要会存在以下两个问题:
(1)过多的SQL语句会降低代码的易读性,另外也容易出现诸如SQL注入(一种网络攻击方式,它利用开发人员编写SQL语句时的疏忽,使用SQL语句实现无账号登录甚至篡改数据库)等安全问题。
(2)开发人员开发时通常会使用SQLite数据库,而在部署时会切换到诸如MySQL等更为健壮的数据库,由于不同数据库需要用到不同的Python库,所以切换数据库就需要对代码中使用的Python库进行同步修改,增加了一定的工作量。
ORM框架
Python中引入了ORM技术。ORM的全称为Object Relational Mapping,表示对象关系映射,它是一种解决面向对象与关系数据库存在的互不匹配现象的技术,用于实现面向对象编程语言中模型对象到关系数据库数据的映射。
ORM 主要实现了以下三种映射关系:
(1)数据表 → Python类
(2)字段(列)→类属性
(3)记录(行)→ 类实例
2.Flask-SQLAlchemy
1.安装
pip install flask-sqlalchemy
pip install Pymysql
2.连接数据库
在操作数据库之前,需要先建立Flask程序与数据库的连接,才能让Flask程序访问数据库,并进一步对数据库中的数据进行操作。
Flask 为Flask-SQLAlchemy扩展包提供了一个配置项SQLALCHEMY_DATABASE_URI,该配置项用于指定数据库的连接,它的值是一个有着特殊格式的URI,URI涵盖了连接数据库所需要的全部信息,包括用户名、密码、主机名、数据库名称以及用于额外配置的可选关键字参数。
dialect+driver:表示数据库类型和驱动程序。
username:表示数据库的用户名。
password:表示数据库的密码。
host:表示主机地址。
port:表示端口号。
database:表示连接的数据库名。
语法:
dialect+driver://username:password@host:port/database
连接数据库的示例代码:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 通过URI连接数据库
app.config['SQLALCHEMY_DATABASE_URI']='mysql+pymysql://root:123456@localhost/blog'
db = SQLAlchemy(app)
# 动态追踪数据库的修改,不建议开启
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
3.定义模型
Flask中的模型以Python类的形式进行定义,所有的模型类都需要继承Flask-SQLAlchemy提供的基类db.Model,通常保存在model.py文件中。
按照ORM的映射关系将模型类转换成数据表,数据表的名称具体分成:
(1)若模型类中包含类属性__tablename__,则会将类属性__tablename__的值作为数据表的名称。
(2)若模型类中没有包含类属性__tablename__,则会将模型类的名称按照一定的规则转换成数据表的名称。
转换规则
转换的规则主要有两种情况:
(1)若模型类的名称是一个单词,则会将所有字母转换为小写的单词作为数据表的名称,例如,模型类User对应的数据表为user;
(2)若模型类的名称是多个单词,则会将所有字母转换为小写,以下画线连接的多个单词作为数据表的名称,例如,模型类MyUser对应的数据表为my_user。
语法:
db.Column(name, type_, autoincrement, default, doc, key, index, info, nullable, onupdate, primary_key, server_default, server_onupdate,quote, unique, system, comment, *args, **kwargs)
说明:
- name:表示数据表中此列的名称。若省略,默认使用类属性的名称。
- type_:表示字段的类型。若该参数的值为None或省略,则将使用默认类型NullType,表示未知类型。
- default:表示为字段设置默认值。 index:表示是否为字段创建索引。
- nullable:确定字段的值是否为空,若设置为False,则会在为此列生成DDL时加上NOT
- NULL语句;若设置为True,通常不会生成任何内容。
- primary_key:表示是否将字段设置为主键,若设为True,则会将此列标记为主键列。多个列可以设置此标志以指定复合主键。
- unique:表示该字段是否具有唯一约束,若设为True,则该字段将不允许出现重复值。
- *args:其他位置参数,该参数的值可以为Constraint(表级SQL约束)、ForeignKey(外键)、ColumnDefault、Sequence和Computed Identity类实例。
type_字段类型
定义模型示例代码:
class User(db.Model):
__tablename__='user'
id = db.Column(db.Integer, primary_key=True,autoincrement=True)
username = db.Column(db.String(20), unique=True, nullable=False)
password = db.Column(db.String(20), nullable=False)
4.数据操作
数据库操作主要通过SQL语句进行,而SQLAlchemy提供了将数据映射到对象中的操作方式。这样的方式使得对数据的操作变得简单,无须学习SQL语法即可完成数据操作。
1.添加数据
db.session提供了增加数据的add()和add_all()方法。
add()方法用于向数据库中增加一条记录。
add_all()方法用于向数据库中增加多条记录。
语法:
db.session.add(对象)
db.session.add_all([对象1,...])
示例代码:
@app.route("/add")
def add():
# 创建User类的对象
user1 = User(username="user1", password='123456')
user2 = User(username="user2", password='123456')
user3 = User(username="user3", password='123456')
# 将User类的对象添加到数据库会话中
db.session.add(user1)
db.session.add_all([user2, user3])
# 使用commit()方法从会话提交至数据库
db.session.commit()
return "数据添加成功"
2.查询数据
Flask-SQLAlchemy的Model类中提供了一个query属性,模型对象通过访问query属性可以获取一个查询对象(Query类实例),该对象中提供了一些过滤方法和查询方法对查询结果进行过滤和筛选,以便精准地查找。
过滤方法和查询方法是可选的,由于使用过滤方法查询后会返回新的查询对象,所以过滤方法和查询方法可以叠加使用,也可以单独使用查询方法。
语法:
模型类.query.<过滤方法>.<查询方法>
过滤方法
查询方法
查询用户 - 示例代码:
@app.route("/query")
def query():
users = User.query.all()# 查询全部记录
user1 = User.query.first()# 查询第一条记录
user2 = User.query.get(2)# 返回主键值2对应的记录
# 过滤username等于"user3"的记录
user31 = User.query.filter(User.username=="user3").first()
user32 = User.query.filter_by(username="user3").first()
# 分页查询
page1=User.query.paginate()
return "查询成功"
3.分页查询数据
数据分页是很常见的功能,Flask-SQLAlchemy与Flask-Bootstrap都已经实现好了。paginate()方法用于对查找的所有记录执行分页操作。
(1)page:表示查询的页数。
(2)per_page:表示每页显示信息的条数。
分页对象的items属性可以获取当前页的条目列表。
语法:
模型类.query.paginate(page,per_page)
示例代码:
分页展示用户列表-路由:
# 方式一
@app.route("/query/<int:page>")
def query_page(page):
# 分页查询
users=User.query.paginate(page=page, per_page=3)
return render_template('users.html', users=users)
# 方式二
@app.route("/query_page")
def query_page():
# 分页查询
users=User.query.paginate(per_page=3)
return render_template('users.html', users=users)
分页展示用户列表-模版:
{% extends 'bootstrap/base.html' %}
{# 从 Flask-BootStrap 引入分页功能 #}
{% import 'bootstrap/pagination.html' as pagi %}
{% block title %}数据分页显示{% endblock %}
{% block body %}
<div class="container">
{% for item in users.items %}
<h1>{{ item.username }}</h1>
<p>{{ item.password | safe }}</p>
{% endfor %}
{# 根据分页对象渲染分页按钮 #}
{{ pagi.render_pagination(users) }}
</div>
{% endblock %}
4.修改数据
修改数据的操作建立于查询数据的基础之上。获取到模型对象后,为对象属性进行赋值,调用commit()方法提交至数据库。
示例代码:
@app.route("/update")
def update():
# 获取id为2的用户对象
user = User.query.get(4)
user.password='654321'
# 使用commit()方法从会话提交至数据库
db.session.commit()
return '修改成功'
5.删除数据
删除数据的操作也是建立于查询数据的基础之上。获取到模型对象后,可以使用数据库会话提供的delete()方法,删除完成后同样需要调用commit()方法提交至数据库。
语法:
db.session.delete(对象)
示例代码:
@app.route("/delete")
def delete():
# 获取id为2的用户对象
user = User.query.get(2)
db.session.delete(user)
# 使用commit()方法从会话提交至数据库
db.session.commit()
return '删除成功'
十、项目应用
1.配置文件
定义应用实例的一些参数、属性的,可以通过建立配置类,将这些零碎的配置项整合在一起,并可以通过类继承的方式建立多套配置方案,来实现不同环境下所需的设定。
定义配置类
# 配置类基类,用于定义一些固定的参数
import os
class Config:
SECRET_KEY = '密钥'
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 可在初始化时用于执行自定义操作
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):# 开发环境下所使用的配置类
DEBUG = True
UPLOADED_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'uploads')
CKEDITOR_SERVE_LOCAL = True
CKEDITOR_FILE_UPLOADER = 'main.upload_ckeditor'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost/database_learn'
class TestingConfig(Config):# 测试环境下所使用的配置类
TESTING = True
UPLOADED_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'uploads')
CKEDITOR_SERVE_LOCAL = True
CKEDITOR_FILE_UPLOADER = 'upload_ckeditor'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost/database_learn'
class ProductionConfig(Config):# 生产环境下所使用的配置类
pass
configs = dict( # configs用于映射不同环境下所使用的不同配置类
development=DevelopmentConfig,
testing=TestingConfig,
production=ProductionConfig,
)
加载配置类
from flask import Flask
from flask.helpers import get_env
from config import configs
app = Flask(__name__)
# 加载应用配置
env = get_env()
config = configs.get(env)
app.config.from_object(config)
config.init_app(app)
2.项目结构
应用“工厂”函数
import os
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_ckeditor import CKEditor
from flask_sqlalchemy import SQLAlchemy
from config import configs
from .database import DatabaseHelper
from .common import *
from .errors import *
# 对象在函数外创建,以便在其他文件中引用
bootstrap = Bootstrap()
db = SQLAlchemy()
# 创建数据库助手类实例
dh = DatabaseHelper(db)
def create_app(env):
app = Flask(__name__)
# 加载应用配置
config = configs.get(env)
app.config.from_object(config)
config.init_app(app)
# 初始化数据库操作实例
db.init_app(app)
# 初始化Flask-Bootstrap
bootstrap.init_app(app)
# 初始化CKEditor
ckeditor = CKEditor()
# 初始化Flask-CKEditor
ckeditor.init_app(app)
bootstrap_cdns = app.extensions['bootstrap']['cdns']
bootstrap_cdns['bootstrap'] = bootstrap_cdns['local']
bootstrap_cdns['jquery'] = bootstrap_cdns['local']
# 将函数注册到全局对象中
app.add_template_global(range_list, 'global_test')
os.makedirs(app.config['UPLOADED_PATH'], exist_ok=True)
# 将各模块的蓝图(Blueprint)注册到应用实例
from .main import main
# url_prefix参数可以为用户模块添加上级位置
app.register_blueprint(main, url_prefix='/main')
# from .admin import admin, admin_user
# app.register_blueprint(admin, url_prefix='/admin')
# app.register_blueprint(admin_user, url_prefix='/admin/user')
# 将所有模型类注册到模板全局变量,以便于后续调用
# for cls in dh.get_all_model_classes():
# app.add_template_global(cls)
return app
应用管理入口
from flask.helpers import get_env
from app import create_app
# 根据相应的环境创建应用实例
env = get_env()
app = create_app(env)
if __name__ == '__main__':
app.run()
3.模块化开发
在简单的demo或演示中,可以采用单一文件的方式进行开发。采用这种方式固然简单,可以快速达成目标。但这种方式不适合用于完成大型项目,也不利于后续的维护。
通过使用蓝图(Blueprint)实现模块化开发,可以解决以上问题。
创建蓝图初始化
# main/__init__.py
from flask import Blueprint
main= Blueprint('main', __name__)
# 加载相关的模型、视图、错误视图(如果有)
from . import models, views, errors
注意:
在“models.py”中定义的模型将会因为被引入,而被“db”对象捕捉到,实现表结构的初始化;而“views.py”中定义的视图函数,将会因为使用“route”装饰器而被初始化代码中的“user”蓝图对象捕捉到,从而实现路由的注册;“errors.py”中的错误视图函数同理。最后完成了模型、视图、错误视图的加载(或注册)。
注册蓝图
# app/__init__.py
# 将各模块的蓝图(Blueprint)注册到应用实例
from .main import main
# url_prefix参数可以为用户模块添加上级位置
app.register_blueprint(main, url_prefix='/main')
4.Flask-Login
Flask-Login 是一个 Flask 扩展,用于处理用户身份验证和用户会话管理。它简化了用户认证和会话管理的实现,特别适用于构建需要用户登录功能的 Web 应用程序。
安装依赖
与安装Flask的操作一致,打开命令提示符窗口,输入以下命令。
pip install flask-login==0.4.1
用户认证后可以在其他模块(视图函数)中,调用用户模块公共方法(common.py)中的get_current_user()函数获取当前登录的用户。
在某些页面中,例如注销页面、查看当前登录的用户信息页面等,需要用户登录后才可访问。难道每一个需要使用当前用户的视图函数都需要添加一段“if current_user is not None”的代码吗?
答案是不需要,使用Flask-Login,只要添加“login_required”装饰器即可。