Flask 初识
django是个大而全的框架,flask是一个轻量级的框架
django内部为我们提供了非常多的组件,orm/session/cookie/admin/modelform/form/路由/视图/模版/中间件/分页/auth/contenttype/缓存/信号/多数据库连接
flask框架本身没有太多的功能,路由/视图/模版(jinja2)/session/中间件,第三方组件非常齐全
注意事项:
- django的请求处理是逐一封装和传递的
- flask的请求是利用上下文管理来实现的
flask本身是没有WSGI,是依赖于第三方的werkzeug实现的,重要的两个一个是werkzeug(WSGI)、一个是jinja2(模版渲染)
Flask 目录
-
flask目录结构
__init__.py :Flask框架的初始模块,创建Flask应用程序对象的入口。
app.py:定义了Flask应用的核心类,包括请求处理、URL路由、视图函数等。
globals.py:定义了一些全局变量和常量。
cli.py:定义了命令行接口相关的功能,如启动服务器等。blueprints.py:实现了蓝图(Blueprint)功能,用于组织和管理应用的模块化组件。
request.py:封装了请求对象,提供了方便的访问请求数据的方法。
session.py:封装了会话(Session)功能,用于在客户端和服务器之间存储和传递数据。
template.py:模板引擎相关的代码,用于渲染生成HTML页面。
views.py:定义了基于类的视图的实现。
helpers.py:定义了各种辅助函数,如URL生成、重定向等。
-
flask文件依赖关系
__init__.py是Flask的入口模块,它导入了其他核心模块和各种类、函数以及对象。
app.py依赖于init.py和其他模块,它使用了其中定义的类和函数来实现请求处理、路由等功能。
globals.py中定义的全局变量和常量可以被其他模块导入和使用。
cli.py依赖于init.py和其他模块,它通过命令行接口来管理应用程序的各种操作和配置。
blueprints.py提供了蓝图功能,其他模块可以导入该模块,并使用其中定义的类和函数来创建和注册蓝图。
request.py封装了请求对象相关的功能,其他模块可以导入其中的类和函数来处理请求数据。
session.py提供了会话功能,其他模块可以导入其中的类和函数来进行会话管理。
template.py实现了模板引擎功能,其他模块可以导入其中的类和函数来渲染生成HTML页面。
views.py实现了基于类的视图,其他模块可以导入其中的类和函数来定义和处理视图。
helpers.py提供了各种辅助函数,其他模块可以导入其中的函数来进行操作和功能扩展。
Flask中的werkzeug
Werkzeug是一个Python库,用于开发Web应用程序。它是一个WSGI(Web Server Gateway Interface)工具包,提供了一系列实用功能来帮助开发者处理HTTP请求、响应、URLs等等。Werkzeug的设计非常灵活,可以用作构建各种Web框架的基础。
Werkzeug的特性包括:
- 请求和响应对象:Werkzeug为HTTP请求和响应提供了易于使用的包装器,使得开发者可以更方便地处理这些请求和响应。
- URL路由:Werkzeug提供了强大的URL路由功能,能够帮助开发者将URL映射到相应的处理函数。(文末进行举例说明)
- 错误处理:Werkzeug提供了异常处理机制,可以方便地处理HTTP错误,并且提供了一个交互式的调试器,使得在开发过程中调试错误更加方便。
- HTTP工具:Werkzeug还提供了一些其他的HTTP相关的工具,比如处理cookies、文件上传等。
# django和flask内部都没有实现socket,而是wsgi实现。
# wsgi是web服务网管接口,他是一个协议,实现它的协议的有:wsgiref/werkzurg/uwsgi
# django之前
from wsgiref.simple_server import make_server
def run(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8000, run)
httpd.serve_forever()
# Flask之前
from werkzeug.serving import run_simple
from werkzeug.wrappers import Response
def func(environ,start_response):
response = Response('您好!')
print("请求来了")
return response(environ, start_response)
if __name__ == '__main__':
run_simple("127.0.0.1",5000,func)
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [15/May/2024 10:34:43] "GET / HTTP/1.1" 200 -
请求来了
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self, environ, start_response):
# 返回XXX类对象(执行flask源码功能)
return 'XXX'
app = Flask()
if __name__ == '__main__':
# 将一个实例像函数一样调用,需要在类中实现__call__方法
run_simple("127.0.0.1", 5000, app)
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ,start_response):
# 返回XXX类对象
return 'XXX'
def run(self):
# 将一个实例像函数一样调用,需要在类中实现__call__方法
run_simple("127.0.0.1",5000,self)
app = Flask()
if __name__ == '__main__':
app.run()
# 快速Flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "hello world"
if __name__ == '__main__':
app.run()
from werkzeug.wrappers import Request, Response
def application(environ, start_response):
# 这定义了一个函数,名为application。这个函数符合WSGI的规范,是一个典型的WSGI应用。
# 它接受两个参数,environ是一个包含所有HTTP请求信息的字典,start_response是一个发送HTTP响应的回调函数。
request = Request(environ)
text = 'Hello, %s!' % request.args.get('name', 'World')
response = Response(text, mimetype='text/plain')
return response(environ, start_response)
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, application)
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://localhost:4000
Press CTRL+C to quit
127.0.0.1 - - [13/May/2024 11:32:13] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/May/2024 11:32:13] "GET /favicon.ico HTTP/1.1" 200 -
# werkzeug的路由系统
from werkzeug.routing import Map, Rule
from werkzeug.wrappers import Request, Response
# 创建一个 URL 映射
url_map = Map([
Rule('/', endpoint='hello'), # 将根路径 / 映射到 'hello' 端点
Rule('/bye', endpoint='bye') # 将 /bye 路径映射到 'bye' 端点
])
# 创建处理函数字典
view_functions = {
'hello': lambda: Response('Hello, World!'),
'bye': lambda: Response('Goodbye, World!')
}
# 创建 WSGI 应用
def application(environ, start_response):
request = Request(environ)
urls = url_map.bind_to_environ(request.environ) # 将环境绑定到 URL 映射
endpoint, args = urls.match() # 从 URL 映射中匹配请求路径
response = view_functions[endpoint]() # 使用相应的处理函数处理请求
return response(environ, start_response)
# 在主程序中运行 WSGI 服务器
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, application)
总结:
flask框架是基于werkzeug的wsgi实现的,flask自己没有wsgi
用户请求一但到来,就会调用app.call方法
写flask的标准流程:
- 创建Flask对象
- 路由和视图函数
- app.run() 运行
Flask 示例
登录示例
from flask import Flask,render_template,request,redirect,jsonify
app = Flask(__name__)
@app.route("/login",methods=['GET',"POST"])
def login():
if request.method == "GET":
return render_template("login.html")
user = request.form.get("user")
pwd = request.form.get("pwd")
if user == "tang" and pwd == "123456":
return redirect("/index")
error_msg = "用户名或者密码错误"
# return render_template("login.html",**{"error": error_msg})
return render_template("login.html",error=error_msg)
@app.route("/")
def first():
return jsonify({"code":1000,"data":"index"})
@app.route("/index")
def index():
return "首页"
if __name__ == '__main__':
app.run()
* Serving Flask app '__main__'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [13/May/2024 13:38:13] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [13/May/2024 13:38:16] "GET /login HTTP/1.1" 200 -
127.0.0.1 - - [13/May/2024 13:38:22] "POST /login HTTP/1.1" 302 -
127.0.0.1 - - [13/May/2024 13:38:22] "GET /index HTTP/1.1" 200 -
127.0.0.1 - - [13/May/2024 13:38:30] "POST /login HTTP/1.1" 200 -
<!-- Flask\templates\login.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Flask login 示例</title>
</head>
<body>
<h1>用户登录界面</h1>
<form method="post">
<input type="text" name="user"></input>
<input type="text" name="pwd"></input>
<input type="submit" value="提交"> <span style="color: red">{{error}}</span>
</form>
</body>
</html>
页面数据编辑
获取查询字符串的两种方式:
- 第一种使用 request.args.get 去获得
- nid = request.args.get("nid")
- 第二种使用\<int:nid> 加上handler传参的方式去获得,并将参数转化成int类型,不加就是字符串类型
- @app.route("/del/\<int:nid>")
url的跳转方式有两种:
@app.route("index",endpoint="idx")
def index():
pass
def a():
# 直接使用redirect + 路由的方式跳转
return redirct("/index")
def b():
# 使用redirect + url_for + endpoint别名的方式进行跳转
return rediect(url_for("idx"))
from flask import Flask,render_template,request,redirect,url_for
app = Flask(__name__)
data_dict = {
"1":{"name":"狗剩",'age':18,"gender":"男"},
"2":{"name":"钢蛋",'age':20,"gender":"女"},
"3":{"name":"铁锤",'age':40}
}
@app.route("/index",endpoint='idx')
def index():
return render_template("index.html",data_dict=data_dict)
@app.route("/edit",methods=["GET","POST"])
def edit():
# 拿到的都是字符串类型
nid = request.args.get("nid")
if request.method =="GET":
info = data_dict[nid]
return render_template("edit.html",info=info)
user = request.form.get('name')
age = int(request.form.get('age'))
gender = request.form.get("gender")
data_dict[nid]["name"] = user
data_dict[nid]["age"] = age
data_dict[nid]["gender"] = gender
return redirect(url_for("idx"))
@app.route("/del/<nid>")
def delete(nid):
del data_dict[nid]
print("删除成功")
return redirect(url_for('idx'))
if __name__ == '__main__':
app.run()
总结:
- flask的路由使用的就是装饰器
- 路由参数 路径参数、endpoint、methods
- endpoint不能重名、endpoint不写默认和handler同名
- 使用动态路由
- 提交数据
- request.args
- request.form
- 返回数据
- render_template("模板文件")
- render.jsonify()
- render.redirect('/index') render.redirect(url_for('idx'))
- return "xxxxx"
- 模板处理
- {{ name }}
{% for key,value in term.items() %}
{{ key }}
{{ value }}
{% endfor %}
- 在jinja2中使用注释,不能直接使用html的!的方式
<!-- <td>{{ key }}</td> --> # 不会生效 {# <td>{{ key }}</td> #} # 生效
functools改变装饰器指向
# 使用functools改变装饰器指向
def auth(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@auth
def login():
pass
print(login.__name__)
"""
inner
"""
inner
'\ninner\n'
# 可以使用functools改变装饰器指向
import functools
def auth(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@auth
def login():
pass
print(login.__name__)
"""
login
"""
login
'\nlogin\n'
# 多个装饰器的执行顺序
# 离函数最近的装饰器最先执行,由内向外
import functools
def auth1(func):
# print("auth1")
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
def auth0(func):
# print("auth0")
@functools.wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)
return inner
@auth0
@auth1
def login():
pass
login()
"""
auth1
auth0
"""
# 实际上被包裹上之后,执行的函数变成了inner,但是名字还是login
'\nauth1\nauth0\n'
保存用户的登录信息
flask的session信息在服务端不存储,而是通过加密的方式放在浏览器段进行存储
# 请求任意一个接口都需要session的状态
from flask import Flask, render_template, request, session, redirect, url_for
import functools
app = Flask(__name__)
app.secret_key = "013uherwengnkwdgnkwn"
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
username = session.get('username')
if not username:
return redirect(url_for('login')) # 没定义endpoing ,默认是login()函数名
return func(*args, **kwargs)
return inner
@app.route("/login", methods=['GET', "POST"])
def login():
if request.method == "GET":
return render_template("login.html")
user = request.form.get("user")
pwd = request.form.get("pwd")
if user == "tang" and pwd == "123456":
session['username'] = 'tang'
return redirect("/index")
error_msg = "用户名或者密码错误"
return render_template("login.html", error=error_msg)
@app.route("/edit")
@auth
def edit():
return 'edit'
@app.route("/del")
@auth
def delete():
return "删除"
@app.route("/index")
@auth
def index():
return "首页"
if __name__ == '__main__':
app.run()
蓝图 (blue print)
帮助我们构建一个业务功能可拆分的目录结构
- 项目目录
- 项目文件
- templates
- static
- views(视图)
- killer.py
- waws.py
- __init__.py
- manage.py (启动文件)
# cd flask_project_demo 项目目录
# flask_project_demo/flask_project/__init__.py
from flask import Flask
from flask_project_demo.flask_project.views.v_killer import killer
from flask_project_demo.flask_project.views.v_waws import waws
def create_app():
app = Flask(__name__)
app.secret_key = "jdoasfajkfpafkadsf"
@app.route('/index')
def index():
return 'index'
# 注册蓝图
# 请求加上前缀 http://127.0.0.1:5000/killer/f2
app.register_blueprint(killer,url_prefix='/killer')
app.register_blueprint(waws,url_prefix='/waws')
return app
# flask_project_demo/manage.py
from Flask.flask_project_demo.flask_project import create_app
app = create_app()
if __name__ == '__main__':
app.run()
# flask_project_demo/flask_project/views/v_killer.py
from flask import Blueprint
killer = Blueprint('v_killer',__name__)
@killer.route('/f1')
def f1():
return 'killer-f1'
@killer.route('/f2')
def f2():
return 'killer-f2'
# flask_project_demo/flask_project/views/v_waws.py
from flask import Blueprint
waws = Blueprint('v_waws',__name__)
@waws.route('/f3')
def f1():
return 'waws-f3'
@waws.route('/f4')
def f2():
return 'waws-f4'