python轻量级框架-flask

flask简述

Flask 是 Python 生态圈中一个基于 Python 的Web 框架。其轻量、模块化和易于扩展的特点导致其被广泛使用,适合快速开发 Web 应用以及构建小型到中型项目。它提供了开发 Web 应用最基础的工具和组件。之所以称为微框架,是因为它与一些大型 Web 框架(如 Django)不同,并不捆绑数据库管理、表单验证等功能,而是保持了极简的核心,只包含了路由、模板引擎和WSGI服务器的基本功能,使开发者可以根据需求选择合适的扩展来构建应用。掌握 Flask 的基础之后可以深入学习如何实现更复杂的功能,例如用户认证、API 版本管理、性能优化,以及如何与前端框架(如 Vue.js 或 React)进行集成,以构建现代的全栈应用。Flask 的设计哲学是简单优先、灵活性强,让开发者对应用的构建过程有更多的控制。Flask 由 Armin Ronacher 开发,基于两个核心工具库:Werkzeug和Jinja2。

Werkzeug是一个 WSGI工具包,用于处理 HTTP 请求和响应。

Jinja2是一个灵活的模板引擎,用于生成动态 HTML 页面。

Flask 的架构是典型的 MVC(Model-View-Controller)风格中的一种实现。这种架构可以有效分离应用的业务逻辑、数据管理和视图展示,有助于保持代码的清晰性和模块化,核心功能主要包括:

基本要点

1. 路由系统

Flask 使用 @app.route() 装饰器定义 URL 路由,这使得定义一个请求的处理函数变得非常简洁。每个 URL 路径都对应一个视图函数,它负责处理来自客户端的请求并返回合适的响应。类似于之前提到过的fastapi:

from flask import Flask
app = Flask(__name__)# 路由与视图函数一一对应,程序实例需要知道每个url请求所对应的运行代码是谁,所以程序中必须要创建一个url请求地址到python运行函数的一个映射
@app.route("/hello") # 将路径 /hello 映射到函数 hello_world,当用户访问这个路径时,浏览器会得到 Hello, World! 的响应,url映射的函数,要传参则在上述route(路由)中添加参数申明
def hello_world():return "Hello, World!"# 注意:如果ip设置成0.0.0.0(不仅监听本地端口)且端口对外开发,那么在任意主机上都可以访问该地址,请确保你本地数据处于安全状态不受攻击
if __name__ == "__main__":app.run(host="127.0.0.1", port=8080, debug=False)

访问上述路径是会打印出该函数结果:

2. 请求和响应处理

Flask 对 HTTP 请求和响应的处理非常灵活。通过 flask.request 对象,可以访问请求的所有细节,如查询参数、表单数据、上传的文件等。对于响应,开发者可以使用 flask.Response 类来自定义 HTTP 响应。

from flask import Flask, requestapp = Flask(__name__)#注意路由路径不要重名,映射的视图函数也不要重名,methods表示请求方式
@app.route("/greet", methods=["GET", "POST"])
def greet():if request.method == "POST":name = request.form.get("name")return f"Hello, {name}!"return """<form method="POST">Name: <input type="text" name="name"><input type="submit" value="Submit"></form>"""if __name__ == "__main__":app.run(debug=False)

访问上述路径时如下图:

3、路由

可以在路径内以/<参数名>的形式指定参数,默认接收到的参数类型是string

以下为flask框架自带的转换器,可以置于参数前将接收的参数转化为对应类型
string 接受任何不包含斜杠的文本
int 接受正整数
float 接受正浮点数
path 接受包含斜杠的文本

当然也可以自定义转换器(pip install werkzeug):

from werkzeug.routing import BaseConverter #导入转换器的基类,用于继承方法
from flask import Flaskapp = Flask(__name__)# 自定义转换器类
class RegexConverter(BaseConverter):def __init__(self,url_map,regex):# 重写父类定义方法super(RegexConverter,self).__init__(url_map)self.regex = regexdef to_python(self, value):# 重写父类方法,后续功能已经实现好了print('to_python方法被调用')return value# 将自定义的转换器类添加到flask应用中
# 具体过程是添加到Flask类下url_map属性(一个Map类的实例)包含的转换器字典属性中
app.url_map.converters['re'] = RegexConverter
# 此处re后括号内的匹配语句,被自动传给我们定义的转换器中的regex属性
# value值会与该语句匹配,匹配成功则传达给url映射的视图函数
@app.route("/index/<re('1d{10}'):value>")
def index(value):print(value)return "Hello World!"if __name__=='__main__':app.run(debug=False)

4、endpoint

每个实例app中都存在一个url_map,这个url_map中包含了url到endpoint的映射;当request请求传来一个url的时候,会在url_map中先通过rule找到endpoint,然后再在view_functions中根据endpoint再找到对应的视图函数view_func

from flask import Flaskapp = Flask(__name__)# endpoint默认为视图函数的名称
@app.route('/branch')
def test():return 'check success!'
# 可以在路由中修改endpoint,相当于为视图函数起别名(当视图函数名称很长时适用)
@app.route('/hello',endpoint='hello_test')
def hello_world():return 'Hello World!'if __name__ == '__main__':print(app.view_functions)print(app.url_map)app.run()
  • 可以通过view_functions查看到当前endpoint视图函数的对应情况;
  • 可以通过url_map查看当前urlendpoint的绑定情况;

endpoint默认为视图函数的名称,endpoint相当于给url起一个名字,view_functions内存储的就是url的名字到视图函数的映射,且endpoint在同一个蓝图下也不能重名,要注意即使修改endpoint为其他视图函数名,路由依然是绑定其正下方的第一个视图函数,说明endpoint作用于url,而不是作用于函数名。

5、redirect重定向

在flask 中,重定向是通过flask.redirect(location, code=302)这个函数来实现的,location表示需要重定向的url, 应该配合url_for函数来使用, code表示采用哪个重定向,默认是302,即临时性重定向, 可以修改为301来实现永性重定向;

from flask import Flask,jsonifyapp = Flask(__name__)# endpoint默认为视图函数的名称
#用jsonify库实现将json数据返回给前端
@app.route('/branch')
def test():data={'suatus':'check success!'}return jsonify(data)
# 可以在路由中修改endpoint,相当于为视图函数起别名(当视图函数名称很长时适用)
@app.route('/hello',endpoint='hello_test')
def hello_world():#doing something#...# redirect重定位(服务器向外部发起一个请求跳转)到一个url界面;# url_for给指定的函数构造 URL;# return redirect('/items') 不建议这样做,将界面限死了return redirect(url_for('items'))if __name__ == '__main__':print(app.view_functions)print(app.url_map)app.run()

6、abort函数

自定义的404界面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<!-- 注意图片文件需要放在一个静态文件夹static里 -->
<img src="../static/error404.jpg" alt="" width="1428px" height="57px">
</body>
</html>

二、Flask 的扩展与灵活性

虽然 Flask 是一个微框架,但它具有极强的灵活性,可以自由选择各种扩展来增强功能:

Flask-SQLAlchemy:用于与关系数据库交互,提供 ORM(对象关系映射)支持。是一个流行的 Flask 扩展,它为数据库操作提供了一种更简洁、更 Pythonic 的方式。

Flask-WTF:用于表单处理和验证,简化表单开发。

Flask-Login:用于用户认证与会话管理。

这些扩展可以无缝集成到 Flask 应用中,使开发者在不牺牲灵活性的同时实现复杂的功能。

1、Flask-SQLAlchemy与数据库sqllite交互

#app.py
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///127.0.0.1.db"  # 相对路径
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False  # 禁用模型修改跟踪,节省资源
db = SQLAlchemy(app)
migrate = Migrate(app, db) # 数据库表结构更新迁移class bank_info(db.Model):id = db.Column(db.Integer, primary_key=True)branch_id = db.Column(db.String(80), unique=True, nullable=False)application = db.Column(db.String(120), unique=True, nullable=False)def __repr__(self):return f"<bank_info branch_id={self.branch_id}, application={self.application}>"@app.route("/add_branch_id")
def add_branch_id():# 添加一个新的分支机构branch = bank_info(branch_id="****", application="授信总额:****")# 检查是否已经存在该 branch_id 或 applicationexisting_branch = bank_info.query.filter_by(branch_id=branch.branch_id).first()if existing_branch:return "Branch ID already exists!"db.session.add(branch)db.session.commit()return "Branch added!"@app.route("/check_branch_id")
def get_branch():# 查询所有信息branches = bank_info.query.all()return "<br>".join([f"ID: {branch.id}, branch_id: {branch.branch_id}, application: {branch.application}" for branch in branches])if __name__ == "__main__":with app.app_context():db.create_all()# 在应用上下文中创建数据库表app.run(debug=False)  # 启动

运行后输入路由结果为:

查询结果同理。

注意当表结构需要变更时,需要单独执行命令:

cd ./path/ # 项目目录下,即app.py所在目录
flask db init # 初始化迁移环境,创建一个 migrations/ 文件夹
flask db migrate -m "Add email column to bank_info model"  # -m 用于写一个描述迁移的消息
flask db upgrade # 执行迁移# 如果删除了原来数据库文件需要重建
from your_app import db
db.create_all()#直接查看表结构
sqlite3 your_database.db
.schema bank_info

同时需要更新脚本:

#app.py
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///127.0.0.1.db"  # 使用相对路径
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False  # 禁用模型修改跟踪,以节省资源
db = SQLAlchemy(app)
migrate = Migrate(app, db)#执行数据库迁移class bank_info(db.Model):id = db.Column(db.Integer, primary_key=True)branch_id = db.Column(db.String(80), unique=False, nullable=False)application = db.Column(db.String(120), unique=False, nullable=False)email = db.Column(db.String(120), nullable=True)  # 添加email字段def __repr__(self):return f"<bank_info branch_id={self.branch_id}, application={self.application}, email={self.email}>"@app.route("/add_branch_id")
def add_branch_id():# 添加一个新的分支机构branch = bank_info(branch_id="**1", application="授信总额:*",email="***@163.com")# 检查是否已经存在该 branch_id 或 applicationexisting_branch = bank_info.query.filter_by(branch_id=branch.branch_id).first()if existing_branch:return "Branch ID already exists!"db.session.add(branch)db.session.commit()  # 提交到数据库return "Branch added!"@app.route("/check_branch_id")
def get_branch():# 查询所有信息branches = bank_info.query.all()return "<br>".join([f"ID: {branch.id}, branch_id: {branch.branch_id}, application: {branch.application},email:{branch.email}" for branch in branches])if __name__ == "__main__":
#    with app.app_context():
#       db.create_all()# 在应用上下文中创建数据库表app.run(debug=False)  # 启动

注意,当字段设置unique为True时,每次插入的数据必须要求不一致,否则无法插入。在项目目录下会生成对应的文件:

2、Jinja2 模板引擎的使用

Jinja2 是 Flask 的默认模板引擎,允许开发者将动态数据嵌入到 HTML 中,生成富有交互性的页面。它支持变量、控制结构(如 for 循环和 if 判断)以及宏(类似于函数的代码片段,可以复用):

#模板文件存在于 templates/hello.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Greeting</title>
</head>
<body><h1>Hello, {{ name }}!</h1>
</body>
</html>
  • render_template():可以用于呈现一个开发人员编写的html文件模板
  • request.method用于获取url接收到的请求方式,以此返回不同的响应页面

在 Flask 应用中使用这个模板:

from flask import Flask, render_template# 创建 Flask 应用实例
app = Flask(__name__)# 路由处理函数
@app.route("/greet/<name>",methods=['GET','POST'])#url映射的函数,要传参则在上述route(路由)中添加参数申明
def greet(name):if request.method == 'GET':# 想要html文件被该函数访问到,首先要创建一个templates文件,将html文件放入其中# 该文件夹需要被标记为模板文件夹,且模板语言设置为jinja2return render_template("hello.html", name=name)# 此处欲发送post请求,需要在对应html文件的form表单中设置method为postelif request.method == 'POST':pass# 启动 Flask 应用
if __name__ == '__main__':app.run(debug=False)

render_template() 函数用于渲染 hello.html 模板,并将变量 name 的值传递到模板中,从而动态生成最终的 HTML 页面.

模板中的控制语句:jinja2模板引擎中也可使用if和for控制语句,但是语句需要放置在{% %}中;

if条件判断语句必须包含结束标签{% endif %},其他部分与python中类似,可以与比较运算符> >= < <= == !=结合使用,或与逻辑运算符and,or,not,()结合使用;

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
{% if name==1 %}<h1>恭喜你抽中了一等奖!</h1>
{% if name==2 %}<h1>恭喜你抽中了二等奖!</h1>
{% else %}<h1>恭喜你抽中了三等奖!</h1>
{% endif %}
</body>
</html>
  • for循环控制语句在模板内的用法也和python中类似,遍历的对象可以是字典、元组、列表等,但需要注意的是在模板中无法使用continue和break来对循环进行控制;

  • 常见常量:

  • loop.index: 获取当前的索引值 从1开始
    loop.index0:获取当前的索引值 从0开始
    loop.first: 判断当前是否是第一次迭代, 是返回True否则返回False
    loop.last: 判断当前是否是最后一次迭代, 是返回True否则返回False
    loop.length: 序列的长度

    {% for 目标 in 对象 %}

    目标


    {% endfor %}
    • {% for item in list %}
    • {

      { item }}


    • 当前的索引是:{

      { loop.index }}


    • 当前的索引是:{

      { loop.index0 }}


    • 当前是否是第一次迭代:{

      { loop.first }}


    • 当前是否是最后一次迭代:{

      { loop.last }}


    • 前序列的长度:{

      { loop.length }}

宏的定义、调用与导入

  • 宏的定义是为了将前端模板中需要反复创建的模块变成一个方便调用的“函数”,这一操作类似于python中创建函数,也可以传参,但不能有返回值

  • 宏的定义以macro标志开始,以endmacro结束,同样需要在{% %}中进行。

    {% macro input1() %}


    {% endmacro %}

    {% macro input2(name, value=‘’, type=‘text’, size=30) %}


    { type }}" name="{

    { name }}" value="{

    { value }}" size="{

    { size }}">



    {% endmacro %}

  • 宏的调用同样类似于函数的调用,如果未采用关键字传参则要注意顺序。

    {

    { input1() }}

    {

    { input2() }}

    {

    { input2(‘username’) }}

    {

    { input2(‘username’, value=‘cheng’, type=‘password’, size=50) }}

宏的导入可以使用语句import 模板文件名 as 别名或from 模板文件名 import 宏名称,就像python中库和包的导入一样;
将宏单独定义在一个html模板文件中后,就可以通过导入这个模板文件来调用里面的所有宏,导入过程同样在{% %}中进行,调用过程在{ { }}在进行。

<!-- 上述宏定义在了一个index3.html的文件中 -->
{% import 'index3.html' as index3 %}<div><!-- 调用导入的宏模板文件中的宏,实现登录页面构建 --><p>用户名:{{index3.input2('username')}}</p><p>密码:{{index3.input2('password',type='password')}}</p><p>登录:{{index3.input2('submit',type='submit',value='登录')}}</p></div><!-- 另一种导入方式 -->
{% from 'index3.html' import input2 %}<div><!-- 此时直接调用input2即可 --><p>用户名:{{input2('username')}}</p><p>密码:{{input2('password',type='password')}}</p><p>登录:{{input2('submit',type='submit',value='登录')}}</p></div>

3、构建完整的 CRUD 应用

一个典型的 Web 应用需要对数据进行创建(Create)、读取(Read)、更新(Update)和删除(Delete),这被称为 CRUD 操作。借助 Flask可以很方便地构建一个支持 CRUD 操作的应用:

#用 Flask 构建一个完整的 CRUD API。通过 HTTP 的 POST、GET、PUT 和 DELETE 方法,客户端可以实现对 Item 对象的创建、读取、更新和删除操作from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemyapp = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///crud.db"
db = SQLAlchemy(app)class Item(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(80), unique=True, nullable=False)def to_dict(self):return {"id": self.id, "name": self.name}@app.route("/items", methods=["POST"])
def create_item():name = request.json.get("name")item = Item(name=name)db.session.add(item)db.session.commit()return jsonify(item.to_dict()), 201@app.route("/items", methods=["GET"])
def get_items():items = Item.query.all()return jsonify([item.to_dict() for item in items])@app.route("/items/<int:item_id>", methods=["PUT"])
def update_item(item_id):item = Item.query.get_or_404(item_id)item.name = request.json.get("name")db.session.commit()return jsonify(item.to_dict())@app.route("/items/<int:item_id>", methods=["DELETE"])
def delete_item(item_id):item = Item.query.get_or_404(item_id)db.session.delete(item)db.session.commit()return "", 204if __name__ == "__main__":with app.app_context():db.create_all()app.run(debug=False)

运行上行脚本,打开postman新建一个文件执行操作:

点击send如果成功则会出现:

在浏览器里面输入对应路由显示为:

表明数据新建成功。

如果想要更新或者是删除操作则分别选择put和DELETE选项,发送给对应请求,请求体路由输入对应ID即可,请求体输入对应新的名称:

4、Flask 中间件与蓝图

4.1. 中间件

中间件(Middleware)是位于请求与响应之间的代码,用于对请求或响应进行处理。Flask 的中间件可以用来做很多事情,例如:记录日志、修改请求、或在响应中增加自定义的 HTTP 头等:

from flask import Flask, jsonifyapp = Flask(__name__)# 定义在请求处理之前执行的函数
@app.before_request
def before_request_func():print("Before request is called")# 定义在响应返回之前执行的函数
@app.after_request
def after_request_func(response):print("After request is called")# 在这里你可以修改响应(如添加头信息等)response.headers["X-Custom-Header"] = "Hello World"return response# 一个简单的路由
@app.route('/')
def home():return jsonify(message="Hello, Flask!")if __name__ == '__main__':app.run(debug=True)

@app.before_request 装饰器用于定义在每个请求处理之前执行的函数。这通常用于一些预处理任务,比如验证用户身份、记录日志、设置请求上下文等。

  • 该函数表示每一次请求之前,可以执行某个特定功能的函数;
  • 可以存在多个before_request装饰器,执行顺序是由上到下,先绑定的先执行
  • 并且先执行 flask app 的 before_request, 再执行 blueprint 的 before_request;
  • 执行时机:当客户端发出请求时,Flask 在请求被处理之前调用before_request_func
  • 常见用途:检查用户是否已经登录,处理跨域请求(CORS),或者设置数据库连接等。

@app.after_request 装饰器用于定义在每个请求处理完成后、响应返回给客户端之前执行的函数。这个函数接受一个 response 参数,可以对响应进行修改或者执行一些收尾操作。

  • 执行时机:当请求处理完成并且响应准备返回时,Flask 会调用after_request_func。你可以对response进行修改(例如添加 headers、修改响应内容等)。

  • 常见用途:设置响应头、日志记录、性能监控、跨域处理等。

  • @app.before_request@app.after_request是应用级别的装饰器,适用于每一个请求。

  • @app.after_request装饰器中的函数必须返回一个响应对象。如果没有对response做任何修改,至少需要返回原始的response对象。

@app.before_first_request:与before_request的区别是,只在第一次请求之前调用;

  • 该函数表示第一次请求之前可以执行的函数;
  • 执行顺序同样也是先绑定的先执行

@app.teardown_request:每一次请求之后都会调用;

  • 该函数接收一个参数,该参数是服务器出现的错误信息;
  • 执行顺序也是先绑定的后执行
  • 只有在请求上下文被 pop 出请求栈的时候才会直接跳转到teardown_request;
  • 所以在被正常调用之前,即使某次请求有抛出错误,该请求也都会被继续执行, 并在执行完后返回 response;
4.2. 蓝图(Blueprint)

当应用规模变大时,代码结构的组织变得至关重要。Flask 提供了 蓝图(Blueprint)组件来帮助开发者将应用模块化。在 Flask 中,应用可以通过蓝图将不同的视图函数和路由分离开来,使得代码更加结构化和易于管理。蓝图的作用是将视图函数、错误处理、静态文件、模板等逻辑与主应用程序分开,便于进行模块化开发。在一个大型应用程序中,蓝图可以将不同的功能区域分离,使得不同的功能部分有独立的管理。在 Flask 应用中,可以通过app.register_blueprint()将蓝图注册到应用程序中,之后蓝图中的路由和视图就会成为主应用的一部分。

将用户相关的路由逻辑组织在一个独立的蓝图中,并在主应用中注册这个蓝图,从而使代码结构更加清晰和模块化:,一个实例下的蓝图对象不可重名。

# 创建蓝图:user.py
from flask import Blueprint# 创建一个名为 "user" 的蓝图模块
user_bp = Blueprint("user", __name__)# 定义蓝图中的一个路由
@user_bp.route("/user/<username>")
def show_user(username):return f"User: {username}"# 注册蓝图
from flask import Flask
from user import user_bp
# from article import article_bpapp = Flask(__name__)# 注册多个蓝图
app.register_blueprint(user_bp)
# app.register_blueprint(article_bp)if __name__ == "__main__":app.run(debug=True)

blueprint对象的工作方式与Flask对象类似,但其不是一个单独的应用;
每拓展一个蓝图对象,就要在主路由文件下添加一行注册代码;
蓝图对象内记录了当前模块下的所有视图函数,故视图函数不可与蓝图对象同名;
在蓝图内需要通过蓝图对象来定义路由和调用其他装饰器,蓝图对象定义的路由处于休眠状态,在蓝图被注册时才成为程序的一部分。

url_prefix设置蓝图前缀

一般在蓝图对象定义时添加,为当前蓝图下的所有视图函数添加统一的前缀,这样不同蓝图下的视图函数的url就不易发生重复;

new_list = Blueprint('test',__name__,url_prefix='/index')
3 添加前缀后,加载该路由的url就变为"/index/test":
@new_list.route('/test')
def test():return 'hello dog!'

5、url与视图函数的绑定

实现url与视图函数的绑定,除了使用路由装饰器@app.route,还可以通过add_url_rule(rule,endpoint=None,view_func=None)方法,其中:
rule:设置的url
endpoint:给url设置的名称
view_func:指定视图函数的名称

from flask import Flask,url_forapp = Flask(__name__)@app.route('/branch',endpoint='branch')
# 底层其实是用add_url_rule实现的
def check_branch():return 'branch Hive is null'def my_test():return '这是测试查询页面'
app.add_url_rule(rule='/test',endpoint='test',view_func=my_test)# 请求上下文只有在发送request请求时才会被激活,激活后request对象被设置为全局可访问
# 其内部封装了客户端发出的请求数据报文
# 此处是主动生成一个临时的测试请求上下文
with app.test_request_context():print(url_for('test')) # 输出结果为/testif __name__ == '__main__':app.run(debug=False)

6、类试图和自定义装饰器

视图还可以由类来实现,即标准类视图:
定义时需要继承flask的views.View这一基类;
每个类视图内必须包含一个dispatch_request方法,每当类视图接收到请求时都会执行该方法,返回值的设定和视图函数相同;
视图函数可以通过@app.route和app.add_url_rule来进行注册(映射到url),但类视图只能通过app.add_url_rule来注册,注册时view_func不能直接使用类名,需要调用基类中的as_view方法来为自己取一个“视图函数名”
采用类视图的最大优势,就是可以把多个视图内相同的东西放在父类中,然后子类去继承父类;而类视图不方便的地方,就是每一个子类都要通过一个add_url_rule来进行注册。

创建一个网站包含三个页面,每个页面中都展示相同的广告:

from flask import Flask,render_template,viewsapp = Flask(__name__)# 定义父视图类继承基类View
class Ads(views.View):def __init__(self):super(Ads, self).__init__()# 实例属性self.context={'ads':'这是一则广告!'}# 定义子视图类继承父类并实现工程
class Index(Ads):def dispatch_request(self):# 字典传参方式==不定长的关键字传参return render_template('class_mould/index.html',**self.context)
class Login(Ads):def dispatch_request(self):# 字典传参方式==不定长的关键字传参return render_template('class_mould/login.html',**self.context)
class Register(Ads):def dispatch_request(self):# 字典传参方式==不定长的关键字传参return render_template('class_mould/register.html',**self.context)# 注册类视图,as_view给类视图起名
app.add_url_rule(rule='/',endpoint='index',view_func=Index.as_view('index'))
app.add_url_rule(rule='/login/',endpoint='login',view_func=Login.as_view('login'))
app.add_url_rule(rule='/register/',endpoint='register',view_func=Register.as_view('register'))if __name__=='__main__':print(app.view_functions)app.run(debug=False)
  • 首页index.html

    Title 这是首页!{

    { ads }}

  • 登录页面login.html

    Title 这是登录页面!{

    { ads }}

  • 注册页面register.html

    Title 这是注册页面!{

    { ads }}

  • 可以通过调用app.view_functions来查看当前的endpoint绑定情况,发现已经变为as_view转化后的类视图:

    {‘static’: <function Flask.init.. at 0x0000024163C46D30>, ‘index’: <function View.as_view..view at 0x0000024164B58CA0>, ‘login’: <function View.as_view..view at 0x0000024164BCB280>, ‘register’: <function View.as_view..view at 0x0000024164BCB310>}

基于方法的类视图:
当需要根据不同请求来实现不同逻辑时,用视图函数需要在内部对请求方法做判断,但若使用方法类视图就可以通过重写其内部方法简单实现;
Flask除了基本类视图,还提供了另一种类视图flask.views.MethodView,在其内部编写的函数方法即是http方法的同名小写映射:

from flask import Flask,render_template,request,viewsapp = Flask(__name__)@app.route('/')
def hello_world():return render_template('index.html')# 定义LoginView类
class LoginView(views.MethodView):# 定义get函数def get(self):return render_template("index.html")# 定义post函数def post(self):username = request.form.get("username")password = request.form.get("password")if username == 'admin' and password == 'admin':return "用户名正确,可以登录!"else:return "用户名或密码错误,不可以登录!"# 注册类视图
# 未设置endpoint,则endpoint默认为as_view设置的类视图名
app.add_url_rule('/login',view_func=LoginView.as_view('loginview'))if __name__ == '__main__':print(app.url_map)app.run(debug=True)
  • 模板文件index.html为:

    Title USERNAME:
    PASSWORD:

装饰器的自定义与使用
装饰器本质上是一个python函数,用来修改或扩展其他函数或方法的行为,而不需要修改它们的源代码,可以让其他函数在不需要做任何代码变得的前提下增加额外的功能,其传入参数一般是函数对象(如视图函数),返回值也是一个函数对象;
装饰器主要用于有切面需求的场景,如插入日志、性能测试、事务处理等与函数功能无关的操作,对于这些需要多次重用的代码,可以将其放置在装饰器里,无需在每个函数中反复编写;
如要在新闻页面前插入登录操作可以这样实现:

from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello_world():return 'Hello World!'# 定义装饰器函数
def user_login(func):def inner():# 替代登录操作print('登录操作!')# 执行传入的函数对象func()# 此处如果return inner(),那么返回的是inner函数的执行结果# 而使用return inner,则返回的是inner函数return inner# 定义新闻页面视图函数news
def news():print('这是新闻详情页!')
# 将news函数作为参数传给装饰器函数
show_news=user_login(news)
# 因为user_login返回inner函数,所以show_news()==inner()
show_news()
# 打印出show_news的真实函数名(为inner)
print(show_news.__name__)if __name__ == '__main__':app.run(debug=True)

运行逻辑:首先将新闻页面函数作为一个参数传给装饰器,装饰器将插入的登录操作与视图函数包装成一个inner函数对象并返回,最后执行该对象便可以实现在新闻页面显示前执行登录操作;
其中登录操作并不是新闻页面函数的功能,且访问每一个新闻页面都应当先执行该操作,固我们将其放置在定义的装饰器中,需要添加该功能的函数使用该装饰器即可;运行结果如下:

登录操作!
这是新闻详情页!
inner

常见标准的装饰器形式:

@user_login
# 定义函数news,该函数将自动被传给装饰器做参数
def news():print('这是新闻详情页!')
# 此时相当于已经执行完news=user_login(news)
news()
print(news.__name__)# show_news=user_login(news)
# show_news()
# print(show_news.__name__)

结果相同,news的函数名也已经变为inner:

登录操作!
这是新闻详情页!
inner

不含参数的函数使用装饰器参考如上,对于带参数的函数同样也可以使用装饰器,Python的可变参数:

def func(*args,**kwargs) :
*:代表元组,长度不限;
**:代表键值对,个数不限;
*args:指用元组传参,元组内包含不定个数的位置参数;
**kwargs:指用字典传参,字典内包含不定个数的关键字参数(键值对);

装饰器的使用可以看作是将函数作为入参传递给另一个函数使用,另一个函数内部嵌套函数定义。

from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello_world():return 'Hello World!'# 定义装饰器函数
def user_login(func):# inner函数接收参数def inner(*args,**kwargs):print('登录操作!')# 执行传入函数时使用inner接收到的参数func(*args,**kwargs)return inner# 不带参的不受影响
@user_login
def news():print(news.__name__)print('这是新闻详情页!')
news()# 带参的定义时预声明接收的参数
@user_login
def news_list(*args):# 获取元组args的第一个元素page=args[0]print(news_list.__name__)print('这是新闻列表页的第'+str(page)+'页!')
# 传递给args的元组即为(5,)
news_list(5)if __name__ == '__main__':app.run(debug=True)

运行结果如下:

登录操作!
news
这是新闻详情页!登录操作!
news_list
这是新闻列表页的第5页!

装饰器不仅可以修改函数的行为,还可以控制函数的返回值,执行其他操作可以修改函数返回的结果。装饰器链,可以将多个装饰器应用到一个函数上,它们会按照从下到上的顺序依次执行。

常见应用场景包括日志记录、权限验证、缓存、性能监控等。

三、数据库访问与数据交互

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23792.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

政安晨【零基础玩转各类开源AI项目】DeepSeek 多模态大模型Janus-Pro-7B,本地部署!支持图像识别和图像生成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 下载项目 创建虚拟环境 安装项目依赖 安装 Gradio&#xff08;UI&#xff09; 运…

在 Mac mini M2 上本地部署 DeepSeek-R1:14B:使用 Ollama 和 Chatbox 的完整指南

随着人工智能技术的飞速发展&#xff0c;本地部署大型语言模型&#xff08;LLM&#xff09;已成为许多技术爱好者的热门选择。本地部署不仅能够保护隐私&#xff0c;还能提供更灵活的使用体验。本文将详细介绍如何在 Mac mini M2&#xff08;24GB 内存&#xff09;上部署 DeepS…

【Godot4.3】基于绘图函数的矢量蒙版效果与UV换算

概述 在设计圆角容器时突发奇想&#xff1a; 将圆角矩形的每个顶点坐标除以对应圆角矩形所在Rect2的size&#xff0c;就得到了顶点对应的UV坐标。然后使用draw_colored_polygon&#xff0c;便可以做到用图片填充圆角矩形的效果。而且这种计算的效果就是图片随着其填充的图像缩…

51单片机-AT24CXX存储器工作原理

1、AT24CXX存储器工作原理 1.1、特点&#xff1a; 与400KHz&#xff0c;I2C总线兼容1.8到6.0伏工作电压范围低功耗CMOS技术写保护功能当WP为高电平时进入写保护状态页写缓冲器自定时擦写周期100万次编程/擦除周期可保存数据100年8脚DIP SOIC或TSSOP封装温度范围商业级和工业级…

Linux网络 网络层

IP 协议 协议头格式 4 位版本号(version): 指定 IP 协议的版本, 对于 IPv4 来说, 就是 4. 4 位头部长度(header length): IP 头部的长度是多少个 32bit, 也就是 4 字节&#xff0c;4bit 表示最大的数字是 15, 因此 IP 头部最大长度是 60 字节. 8 位服务类型(Type Of Service):…

Unity百游修炼(1)——FootBall详细制作全流程

一、引言 游玩测试&#xff1a; Football 游玩测试 1.项目背景与动机 背景&#xff1a;在学习 Unity 的过程中&#xff0c;希望通过实际项目来巩固所学知识&#xff0c;同时出于对休闲小游戏的喜爱&#xff0c;决定开发一款简单有趣的小游戏加深自己的所学知识点。 动机&#…

C语言(13)------------>do-while循环

1.do-while循环的语法 我们知道C语言有三大结构&#xff0c;顺序、选择、循环。我们可以使用while循环、for循环、do-while循环实现循环结构。之前的博客中提及到了前两者的技术实现。可以参考&#xff1a; C语言&#xff08;11&#xff09;-------------&#xff1e;while循…

【1】VS Code 新建上位机项目---C#基础语法

VS Code 新建上位机项目---C#基础语法 1 基本概念1.1 准备工具1.2 新建项目2 C#编程基础2.1 命名空间和类2.2 数据类型2.3 控制台输入输出2.3.1 输入输出: write 与 read2.3.2 格式化 : string.Foramt() 与 $2.3.3 赋值与运算2.4 类型转换2.4.1 数值类型之间的转换:(int)2.4…

SQL:DQL数据查询语言以及系统函数(oracle)

SQL Structured Query Language&#xff0c;结构化查询语言, 是一种用于管理和操作关系数据库的标准编程语言。 sql的分类 DQL&#xff08;Data Query Language&#xff09;&#xff1a;数据查询语言 DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据…

从单片机的启动说起一个单片机到点灯发生了什么下——使用GPIO点一个灯

目录 前言 HAL库对GPIO的抽象 核心分析&#xff1a;HAL_GPIO_Init 前言 我们终于到达了熟悉的地方&#xff0c;对GPIO的初始化。经过漫长的铺垫&#xff0c;我们终于历经千辛万苦&#xff0c;来到了这里。关于GPIO的八种模式等更加详细的细节&#xff0c;由于只是点个灯&am…

提效10倍:基于Paimon+Dolphin湖仓一体新架构在阿里妈妈品牌业务探索实践

1. 业务背景 阿里妈妈品牌广告数据包括投放引擎、下发、曝光、点击等日志&#xff0c;面向运筹调控、算法特征、分析报表、诊断监控等应用场景&#xff0c;进行了品牌数仓能力建设。随着业务发展&#xff0c;基于Lambda架构的数仓开发模式&#xff0c;缺陷日益突出&#xff1a;…

一文详解U盘启动UEFI/Legacy方式以及GPT/MBR关系

对于装系统的老手而说一直想研究一下装系统的原理&#xff0c;以及面对一些问题时的解决思路&#xff0c;故对以前的方法进行原理上的解释&#xff0c;主要想理解其底层原理。 引导模式 MBR分区可以同时支持UEFI和Legacy引导&#xff0c;我们可以看一下微pe制作的启动盘&#…

基于Docker的前端环境管理:从开发环境到生产部署的实现方案

# 基于Docker的前端环境管理&#xff1a;从开发环境到生产部署的实现方案 简介及前端开发环境挑战 简介 是一种容器化平台&#xff0c;可以将应用程序及其依赖项打包为一个容器&#xff0c;提供一种轻量级、可移植的环境。它能够简化开发、部署和运维的流程&#xff0c;提高…

连锁管理系统的五大核心设计及 PHP 源码分享

在当今竞争激烈的连锁商业领域&#xff0c;高效的管理系统是企业脱颖而出的关键。商淘云连锁管理系统凭借其卓越的功能和灵活的架构&#xff0c;为连锁企业提供了强大的运营支持。在这里详细介绍其五大核心设计&#xff0c;并分享相关的 PHP 源码示例。 一、总部进销存管理 &a…

C语言基本知识------指针(4)

1. 回调函数是什么&#xff1f; 回调函数就是⼀个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另⼀个函数&#xff0c;当这个指针被⽤来调⽤其所指向的函数 时&#xff0c;被调⽤的函数就是回调函数。 void qsort(void base,//指针…

MTK-Android13-包安装器PackageInstaller 静默安装实现

目的 我们最终是为了搞明白安装的整个流程。一方面通过安卓系统自带的包安装器来了解PMS 安装流程&#xff1b;另一方面熟悉框架层Framework 针对Android apk 安装流程。 前两篇文章分析了PackagerInstaller 安装流程。 Android13-包安装器PackageInstaller-之apk安装跳转 An…

qt-C++笔记之创建和初始化 `QGraphicsScene` 和 `QGraphicsView` 并关联视图和场景的方法

qt-C++笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 code review! 参考笔记 1.qt-C++笔记之创建和初始化 QGraphicsScene 和 QGraphicsView 并关联视图和场景的方法 2.qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到vie…

《一起打怪兽吧》——自制一款Python小游戏

《一起消灭怪兽吧》——在深夜的屏幕前&#xff0c;你是指引光明的勇者。键盘化作利剑&#xff0c;用方向键在像素战场游走&#xff0c;发射吧&#xff0c;每次击杀都有代码绽放的烟火。这款由Python与Pygame铸就的小游戏&#xff0c;让0与1的世界生长出童真的浪漫。 文章目录…

IDEA中查询Maven项目的依赖树

在Maven项目中&#xff0c;查看项目的依赖树是一个常见的需求&#xff0c;特别是当你需要了解项目中直接或间接依赖了哪些库及其版本时。你可以通过命令行使用Maven的dependency:tree插件来做到这一点。这个命令会列出项目中所有依赖的树状结构。 打开idea项目的终端&#xff…

Maven——Maven开发经验总结(1)

摘要 本文总结了 Maven 开发中的多个关键经验&#xff0c;包括如何根据版本号决定推送到 releases 或 snapshots 仓库&#xff0c;如何在构建过程中跳过测试&#xff0c;父项目如何控制子项目依赖版本&#xff0c;父项目依赖是否能传递到子项目&#xff0c;如何跳过 Maven dep…