Flask 框架引入

发布于:2025-09-12 ⋅ 阅读:(23) ⋅ 点赞:(0)

安装Flask

pip3 install flask	

创建Flask项目

在文件夹下创建app.py,在其中写入以下内容

from flask import Flask

app = Flask(__name__)  # 实例化app对象

@app.route('/',methods=['GET','POST'])  # 路由装饰器,将路由映射到函数,methods是请求方法
def hello_world():
    return 'hello world'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=54188, debug=True)   # 调用run()方法启动服务器,启用debug可以实时显示代码更改后的情况

当同一个路由装饰器装饰多个视图函数时,只会访问第一个路由下的视图函数的返回值,即先到先得

当同一个视图函数被多个路由装饰器装饰时,所有的路由都会正常执行当前视图函数

更改模板\静态资源文件夹等

app = Flask(__name__,static_folder="static",template_folder='templates',...)

修改配置文件

方法零 在启动项目时添加参数
python -m flask run -h 0.0.0.0 -p 54188 --debug 
方法一 在app.py中直接修改
# app.py
app.config['DEBUG'] = True
# print(app.config)	# 打印可配置的项目
# print(app.config.get('DEBUG')) # 打印指定配置的内容
# print(dir(app.config)) # 打印可以调用的方法

Flask框架的配置文件是一个字典形式,直接通过Key定位到待修改的项目,然后进行修改

方法二 添加一个setting.py文件统一管理
# app.py
app.config.from_pyfile('setting.py')

# setting.py
DEBUG = True
方法三 通过对象修改
# 创建一个类,然后在类中添加配置项
class Spt2503(object):
    DEBUG = True
    
class Spt2504(Spt2503):
    HOST = '0.0.0.0'

# 调用命令获取对象的属性,通过其修改配置
app.config.from_object(Spt2504)

查看已有的路由信息

print(app.url_map)  # 打印当前路由列表
print(dir(app.url_map))  # 打印路由中可配置的项目
print(dir(app.url_map.iter_rules))

待整理...

根据已有的函数动态填充指定url

app.url_for(endpoint, \**values)

  • endpoint:路由关联的端点名(通常是视图函数名)
  • **values:URL 变量部分的参数(如 /user/<username> 中的 username

根据端点名称(endpoint)和参数动态生成 URL

url = app.url_for('user_profile', username='alice')

返回JSON格式信息

@app.route('/')
def ok():
    return jsonify({"code":200, "message":"hello"})

通过动态路由实现在链接传值

直接在访问链接的后面添加要传的值

@app.route('/get/<string:name>')  # 在路由中命名
    # 一般常用的类型有 int float string 三种类型
def get(name):
    print()
    return f'hello{name}'

异常处理

@app.errorhandler(404)
def pageNotFound(e):
    return f'页面未找到<hr>{e}'

Flask 请求和响应数据

from flask import Flask
    from flask import request
    from flask import render_template
    from flask import redirect
    from flask import make_response
    app = Flask(__name__)
    @app.route('/login.html', methods=['GET', "POST"])
    def login():
        # 请求相关信息
        # request.method
        # request.args
        # request.form
        # request.values
        # request.cookies
        # request.headers
        # request.path
        # request.full_path
        # request.script_root
        # request.url
        # request.base_url
        # request.url_root
        # request.host_url
        # request.host
        # request.files
        # obj = request.files['the_file_name']
        # obj.save('/var/www/uploads/' + secure_filename(f.filename))
    
        # 响应相关信息
        # return "字符串"
        # return render_template('html模板路径',**{})
		# return redirect('/index.html')
		# response = make_response(render_template('index.html'))
		# response是flask.wrappers.Response类型
		# response.delete_cookie('key')
 		# response.set_cookie('key', 'value')
 		# response.headers['X-Something'] = 'A value'
 		# return response
 		return "内容"
 	if __name__ == '__main__':
 		app.run()

获取前端表单内容

在 Flask 中获取前端表单内容主要有以下两种方式(根据表单的 Content-Type 选择):


  1. application/x-www-form-urlencoded(默认表单类型)

前端示例(HTML):

<form action="/submit" method="POST">
  <input type="text" name="username">
  <input type="password" name="password">
  <button type="submit">提交</button>
</form>

后端处理(Flask):

from flask import request

@app.route('/submit', methods=['POST'])
def handle_form():
    username = request.form.get('username')  # 推荐使用 .get() 避免 KeyError
    password = request.form.get('password')
    return f"Received: {username}, {password}"

  1. application/json(AJAX/前端框架常用)

前端示例(JavaScript):

// 使用 Fetch API 发送 JSON
fetch('/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ username: 'test', password: '123' })
})

后端处理(Flask):

from flask import request, jsonify

@app.route('/submit', methods=['POST'])
def handle_json():
    data = request.get_json()  # 自动解析 JSON
    username = data.get('username')
    password = data.get('password')
    return jsonify({"status": "success", "data": data})

获取前端上传的文件

在 Flask 中处理文件上传需要结合 enctype="multipart/form-data" 表单和 request.files 对象。以下是具体实现方法:


  1. 前端表单(HTML)
<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="file">
  <button type="submit">上传</button>
</form>

  1. 后端处理(Flask)
from flask import request, jsonify
import os

UPLOAD_FOLDER = 'uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)  # 确保上传目录存在

@app.route('/upload', methods=['POST'])
def handle_upload():
    if 'file' not in request.files:
        return jsonify({"error": "No file part"}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({"error": "No selected file"}), 400

    if file:
        file.save(os.path.join(UPLOAD_FOLDER, file.filename))
        return jsonify({"status": "success", "filename": file.filename})

返回信息修改和Cookie\Session设置

可以返回一个元组,这样的元组必须是 (response, status, headers) 的 形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是 一个列表或字典,作为额外的消息标头值

@app.route('/set_res1')
def res1():
    print('元组返回,外面的括号可以省略')
    return 'hello world', 200, {'Content-Type':'text/html;chatset=utf-8'}

也可以通过make_response()来设置返回的信息,包括设置cookie和session

@app.route('/set_res')
def setRes():
    res = make_response('hello')
    res.status_code = 200
    res.header['Content-Type'] = 'text/html;chatset=gbk'
    return res

@app.route('/set_cookie')
def setCookie():
    res = make_response('hello world')
    res.set_cookie('key','value',max_age=3600*24*15) # Cookie一般设置半个月,要删除Cookie,就将事件设置为0
    return res

@app.route('/get_cookie')
def getCookie():
    print(request.cookies)  # 获取全部Cookie
    print(request.cookies.get('key'))	# 获取指定key的Cookie
    return 'hello world'

@app.route('/set_session')
def setSession():
    session['key'] = 'value'
    return '设置成功'

@app.route('/get_session')
def getSession():
    ses = session.get('key')
    return ses

@app.route('/del_session')
def delSession():
    ses = session.pop('key')
    return 'Session删除成功'

session还有许多方法,类似clear(),update()等用法

要设置session,必须设置一个密钥来保护安全,例如在代码中输入app.config['SECRET_KEY'] = 'SECRET_KEY'来设置密钥

设置类视图

...
from flask.views import MethodView
...
class cbv(MethodView):
    def get(self):	# 固定写法,实现GET方法
        return 'hello'

user_view = cbv.as_view('cbv') # cbv是传值的名字(类似前端表单的name),一般等同于类名
app.add_url_rule('/rule',view_func=user_view,methods=['GET','POST'])  # 添加路由

请求\应用上下文(线程局部变量)

请求上下文(request context) request和session

应用上下文(application context) current_app和g

current_app 表示当前运行程序文件的程序实例;g 表示临时存储的对象,每次请求都会重设这个变量,请求完后会清空

请求钩子函数(中间件)

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:

  • before_first_request:在处理第一个请求前运行。
  • before_request:在每次请求前运行。
  • after_request:如果没有未处理的异常抛出,在每次请求后运行。
  • teardown_request:在每次请求后运行,即使有未处理的异常抛出。
  1. before_first_request

作用:在应用处理第一个客户端请求之前运行,通常用于初始化操作(如数据库连接、全局配置等)。

from flask import Flask
app = Flask(__name__)

@app.before_first_request
def initialize():
    print("This runs ONCE before the first request.")

@app.route('/')
def home():
    return "Home Page"

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

行为:启动应用后,首次访问 / 时会打印 "This runs ONCE before the first request.",后续请求不再触发。


  1. before_request

作用:在 每个请求 处理前运行,常用于身份验证、请求预处理等。

from flask import Flask, request
app = Flask(__name__)

@app.before_request
def log_request_info():
    print(f"Request Path: {request.path}")

@app.route('/')
def home():
    return "Home Page"

@app.route('/api')
def api():
    return "API Data"

行为:每次访问 //api 时,均会打印当前请求路径(如 "Request Path: /api")。


  1. after_request

作用:在每次请求 正常完成后(无未处理异常)运行,常用于修改响应(如添加头信息)。

from flask import Flask, make_response
app = Flask(__name__)

@app.after_request
def add_header(response):
    response.headers['X-Custom-Header'] = 'Hello'
    print("Response modified")
    return response

@app.route('/')
def home():
    return "Home Page"

行为:访问 / 后,响应会添加 X-Custom-Header,并打印 "Response modified"。若视图函数抛出异常,此钩子不执行。


  1. teardown_request

作用:在每次请求 结束后 运行(即使有未处理异常),适用于清理资源(如关闭文件、数据库连接)。

from flask import Flask
app = Flask(__name__)

@app.teardown_request
def cleanup(exception):
    if exception:
        print(f"An error occurred: {exception}")
    print("Resource cleanup completed")

@app.route('/error')
def force_error():
    raise ValueError("Demo Error")

@app.route('/')
def home():
    return "Home Page"

行为

  • 访问 /:打印 "Resource cleanup completed"
  • 访问 /error:先打印错误信息,再打印 "Resource cleanup completed"
钩子类型 触发时机 异常影响
before_first_request 首次请求前
before_request 每次请求前 后续流程终止
after_request 请求正常完成后 未处理异常时跳过
teardown_request 请求结束后(无论是否有异常) 始终执行

Jinja2 模板语法

  1. Flask 应用代码 (app.py)
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    # 传递变量到模板
    user = {"name": "Alice", "age": 25}
    numbers = [1, 2, 3, 4]
    return render_template(
        'index.html',  # 模板文件名
        user=user,     # 变量1:字典
        numbers=numbers,  # 变量2:列表
        greeting="Hello World!"  # 变量3:字符串
    )

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

  1. 模板文件 (templates/index.html)
<!DOCTYPE html>
<html>
<head>
    <title>Jinja2 Demo</title>
</head>
<body>
    <h1>{{ greeting }}</h1>  <!-- 输出字符串变量 -->
    
    <!-- 输出字典内容 -->
    <p>User: {{ user.name }}, Age: {{ user.age }}</p>
    
    <!-- 循环输出列表 -->
    <ul>
    {% for num in numbers %}
        <li>Number: {{ num }}</li>
    {% endfor %}
    </ul>

    <!-- 条件判断 -->
    {% if user.age > 18 %}
        <p>This user is an adult.</p>
    {% endif %}
</body>
</html>

Web表单

WTForms支持的HTML标准字段

|
|  |

| ------------------------------------------------------------ | ------------------------------------------------------------ |

WTForms常用验证函数

在这里插入图片描述

应用: 判断密码输入是否一致

# 定义自己的表单类
class MyForm(FlaskForm):
    username = StringField('用户名')
    password = PasswordField('密码')
    qrpassword = PasswordField('确认密码', validators=[EqualTo('password', message='密码不一致')])

@app.route('/form', methods=['GET', 'POST'])
def form():
    form = MyForm(request.form if request.method == 'POST' else None)

    if request.method == 'POST':
        if form.validate():
            print(form.username.data)
            pd = form.password.data
            rpd = form.qrpassword.data
            print(pd, rpd)
            # 成功处理后的逻辑
        else:
            print("表单验证失败:", form.errors)

    # 无论 GET 还是 POST,都传递表单对象到模板
    return render_template('main.html', form=form)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>
        表单 测试
    </title>
</head>
<body>
<form action="/form" method="post">
    {{ form.csrf_token }}
    <input type="text" name="username" id="" value="{{}}">
    <input type="password" name="password" id="">
    <input type="password" name="qrpassword" id="">
    <input type="submit" value="提交">
</form>
</body>
</html>

控制语句

常用的几种控制语句: 模板中的if控制语句

{% if %} {% endif %} 

模板中的for语句

{% for item in samples %} {% endfor %}

类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码 冗余。 Jinja2支持宏,还可以导入宏,需要在多处重复使用的模板代码片段可以写 入单独的文件,再包含在所有模板中,以避免重复。

定义宏

{% macro input() %}
<input type="text"
       name="username"
       value=""
       size="30"/>
{% endmacro %}

调用宏

{{ input() }}

定义带参数的宏

{% macro input(name,value='',type='text',size=20) %}
<input type="{{ type }}"
       name="{{ name }}"
       value="{{ value }}"
       size="{{ size }}"/>
{% endmacro %}

调用宏,并传递参数

{{ input(value='name',type='password',size=40)}}

把宏单独抽取出来,封装成html文件,其它模板中导入使用

文件名可以自定义macro.html

{% macro function() %}
<input type="text" name="username" placeholde="Username">
<input type="password" name="password" placeholde="Password">
<input type="submit">
{% endmacro %}

在其它模板文件中先导入,再调用

{% import 'macro.html' as func %}
{{ func.function() }}

模板继承

模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。

{% block top %}``{% endblock %}标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。

子模板使用extends指令声明这个模板继承自哪?父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()。

父模板

{% block top %}
顶部
{% endblock top %}

{% block content %}
中间
{% endblock content %}

{% block bottom %}
底部
{% endblock bottom %}

子模板

{% extends 'base.html' %}
{% block content %}
需要填充的内容
{% endblock content %}

模板继承使用时注意点:

  • 不支持多继承。
  • 为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
  • 不能在一个模板文件中定义多个相同名字的block标签。
  • 当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。

包含

Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含 (Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。

 {% include 'hello.html' ignore missing %}

包含在使用时,如果包含的模板文件不存在时,程序会抛出 TemplateNotFound异常,可以加上ignore missing关键字。如果包含的 模板文件不存在,会忽略这条include语句。

三者之间的比较

宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。

  • 继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。

  • 宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。

  • 包含(include)是直接将目标模板文件整个渲染出来。

数据库

导包

pip3 install flask-sqlalchemy
pip3 install flask-mysqldb

设置数据库

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

常用的SQLAlchemy字段类型

在这里插入图片描述

常用的SQLAlchemy选项

常用的SQLAlchemy列选项 常用的SQLAlchemy关系选项
在这里插入图片描述
在这里插入图片描述

基本操作

常用的SQLAlchemy查询操作
常用的SQLAlchemy查询过滤器 常用的SQLAlchemy查询执行器
在这里插入图片描述
在这里插入图片描述
创建表
db.create_all()
删除表
db.drop_all()
插入一条数据
ro1 = Role(name='admin')
db.session.add(ro1)
db.session.commit()
#再次插入一条数据
ro2 = Role(name='user')
db.session.add(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()
查询

filter_by()精确查询

返回名字等于wang的所有人

User.query.filter_by(name='wang').all()

first()返回查询到的第一个对象

User.query.first()

all()返回查询到的所有对象

User.query.all()

filter()模糊查询,返回名字结尾字符为g的所有数据。

User.query.filter(User.name.endswith('g')).all()

get(),参数为主键,如果主键不存在没有返回内容

User.query.get()

逻辑非,返回名字不等于wang的所有数据

User.query.filter(User.name!='wang').all()

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

from sqlalchemy import and_
User.query.filter(and_(User.name!='wang',User.email.endswith('163.com'))).all()

逻辑或,需要导入or_

from sqlalchemy import or_
User.query.filter(or_(User.name!='wang',User.email.endswith('163.com'))).all()

not_ 相当于取反

from sqlalchemy import not_
User.query.filter(not_(User.name=='chen')).all()

查询数据后删除

user = User.query.first()
db.session.delete(user)
db.session.commit()
User.query.all()

更新数据

user = User.query.first()
user.name = 'dong'
db.session.commit()
User.query.first()

使用update更新

User.query.filter_by(name='zhang').update({'name':'li'})
db.session.commit()

应用:查询角色

角色和用户的关系是一对多的关系,一个角色可以有多个用户,一个用户只能属于一个角色

查询角色的所有用户

#查询roles表id为1的角色
ro1 = Role.query.get(1)
#查询该角色的所有用户
ro1.us

查询用户所属角色

#查询users表id为3的用户
us1 = User.query.get(3)
#查询用户属于什么角色
us1.role

创建迁移仓库

# 这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python database.py db init
创建迁移脚本

自动创建迁移脚本有两个函数,upgrade()函数把迁移中的改动应用到数据库中,downgrade()函数则将改动删除。自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()downgrade()函数的内容。对比不一定完全正确,有可能会遗漏一些细节,需要进行检查。

# 创建自动迁移脚本
python database.py db migrate -m 'initial migration'
更新数据库
python database.py db upgrade
回退数据库

回退数据库时,需要指定回退版本号,由于版本号是随机字符串,为避免出错,建议先使用python database.py db history命令查看历史版本的具体版本号,然后复制具体版本号执行回退。

python database.py db downgrade 版本号

网站公告

今日签到

点亮在社区的每一天
去签到