Flask入门
Flask介绍和安装
Django
大而全(3.x以后支持异步),Flask
:小而精,插件的支持
异步框架:Sanic
,FastAPI
python异步的orm框架:aiomysql
,aioredis
# 介绍
Flask是一个基于Python开发并且依赖jinja2模板(DTL)和Werkzeug WSGI(符合wsgi协议的web服务器,wsgiref)服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。
# 安装 flask
pip install flask
# 使用
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
flask快速使用,展示用户信息案例
前后端混合项目
登录:返回给前端cookie
登录成功后可以访问首页(不登录不能访问)
点击首页的详情,访问详情(不登录不能访问)
from flask import Flask, request, render_template, session, redirect, jsonify
app = Flask(__name__)
app.secret_key = 'yyqrain'
USERS = {
1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"},
2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"},
3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"},
}
def verification(func_name):
def inner(*args, **kwargs):
if session.get('username') == 'yyq':
res = func_name(*args, **kwargs)
else:
return redirect('/login')
return res
return inner
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
if username == 'yyq' and password == '123':
session['username'] = 'yyq'
return redirect('/home')
else:
return render_template('login.html', err='用户名或密码错误')
@app.route('/home',endpoint='home')
@verification
def home():
return render_template('home.html', user_dict=USERS)
@app.route('/detail/<int:pk>',endpoint='detail')
@verification
def detail(pk):
return render_template('detail.html', info=USERS.get(pk))
if __name__ == '__main__':
app.run()
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<input type="submit" value="提交">{{err}}
</form>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
{% for k,v in user_dict.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息 {{info.name}}</h1>
<div>
{{info.text}}
</div>
</body>
</html>
配置文件写法
# django 配置文件 settings.py 全大写的是它的配置文件
# flask配置文件写法很多种
# 第一种写法
app.secret_key='asfasdf'
app.debug=True
# 第二种:app所有的配置项都在app.config这个字典中
app.config['DEBUG']=True
print(app.config)
# 第三种:仿django的settings.py 写法
app.config.from_pyfile('settings.py')
print(app.config['NAME'])
# 第四种:通过类的方式(推荐)
app.config.from_object('settings.DevelopmentConfig')
print(app.config)
# 其他:
app.config.from_envvar("环境变量名称")
app.config.from_json("json文件名称")
路由系统
flask
的路由写法:基于装饰器,跟djagno
有区别,但是本质其实是一样的,sanic
,fastapi
就是这种路由方式
路由典型写法
@app.route('/index', methods=['GET'], endpoint='index')
def index():
return 'hello'
默认转换器
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
本质
1.装饰器:@route,python特殊语法糖,会把下面的函数当参数传入 order=route(order) 以后调用order本质就是在执行route(order)()
2.route的内层函数---->本质是self.add_url_rule(rule, endpoint, f, **options)
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
3.self是app对象,add_url_rule 其实就Flask类的add_url_rule方法
4.所以我们可以不使用装饰器来注册路由,而自己写
app.add_url_rule('/order/<string:pk>',view_func=order)
app.add_url_rule('/index',view_func=index)
cbv源码
# cbv写法,继承MethodView,跟djagno很像
1.as_view的name是别名,之前写fbv的时候,endpoint是别名,现在写cbv,name它最终就是endpoint,但是必须写,即便写了endpoint
Home.as_view是view函数的内存地址,请求来了执行 self.dispatch_request(),MethodView重写了dispatch_request--》根据请求方式执行视图类中跟请求方式同名的方法
如果视图类继承了View,需要重写dispatch_request
2.app.add_url_rule('/index', view_func=index),路由有个别名,如果不写endpoint,会以函数名作为endpoint
如果是cbv,as_view(name='home') name就是路径的别名,endpoint
3.cbv加装饰器, 在类中写 decorators = (装饰器名字,装饰器名字2,),第一个位置的会放在最下层,多个装饰器执行顺序是从【上往下执行】
4.cbv只允许某个请求方式 methods=['POST']
# 例
from flask.views import MethodView
class Home(MethodView):
methods=['POST']
decorators = (装饰器名字,装饰器名字2,)
def get(self):
return 'home'
def post(self):
return 'home-post'
add_url_rule参数
# 重要
rule:请求的路径,可以使用转换器
endpoint:别名--》反向解析
view_func:视图类.as_view(name='xx')(视图函数内存地址)
methods:允许的请求方式
# 不重要
defaults:字典,给视图函数传默认值
strict_slashes:对URL最后的 / 符号是否严格要求
redirect_to:重定向
# 例
路由1:
app.add_url_rule('/home', view_func=Home.as_view(name='home'))
路由2:
@app.route('/goods',defaults={'name':'lqz'},redirect_to='/home')
def goods(name):
print(name)
return 'goods'
if __name__ == '__main__':
app.run()
模板
# 模板语法:
django 是自己的模板语法,dtl
flask使用第三方,兼容dtl,但是它可以加括号,可以使用[],处理了xss攻击
# xss,csrf,cors分别是什么?
django,flask处理了xss攻击,不存在这个攻击,原理是什么?
使用了html的特殊字符替换,但是如果使用了 |safe 或者Markup ,就不会渲染到页面上
请求响应
from flask import Flask, request,make_response,render_template
app = Flask(__name__)
app.debug=True
def test():
print(request.xxx)
@app.route('/login.html', methods=['GET', "POST"])
def login():
1.请求对象的属性和方法
# request:是全局的request,用起来就当是每个请求都有一个request即可
print(request.method) # 请求方式
print(request.args) # get 请求参数
print(request.form) # post提交的数据
print(request.values) # get,post提交的数据总和
print(request.cookies) # cookies
print(request.headers) # 请求头
print(request.path) # 路径
print(request.full_path) # 全路径
print('-----',request.script_root)
# request.url 带域名带参数的请求路径
print(request.url) # 带服务器地址的全路径
# request.base_url 带域名请求路径
print(request.base_url) # 不带地址的全路径
# request.url_root 域名
print(request.url_root) # 域名+端口
# request.host_url 域名
print(request.host_url) # 域名+端口
# request.host
print(request.host) # 不带http的域名+端口
from werkzeug.datastructures import FileStorage
print(type(request.files.get('files')))
2.响应对象:四件套
##1 向浏览器中写入cookie,四件套都可以使用make_response包裹一下变成响应对象
res=make_response(render_template('home.html'))
res.set_cookie('name','lqz')
# delete_cookie
## 2 向浏览器中写响应头
# res.headers['X-Something'] = 'A value'
return res
@app.route('/register.html', methods=['GET', "POST"])
def register():
test()
return "内容"
if __name__ == '__main__':
app.run()
session的使用和原理
session执行原理
flask的session源码分析
# app.session_interface---> SecureCookieSessionInterface()类的对象
-open_session:请求来了,从cookie中取出字符串,把字符串反序列化成session对象
-save_session:请求走,把session对象,序列化成字符串,放到cookie中
# open_session
def open_session(self, app, request):
s = self.get_signing_serializer(app)
if s is None:
return None
val = request.cookies.get(app.session_cookie_name)
if not val:
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
# save_session
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
if not session:
if session.modified:
response.delete_cookie(
app.session_cookie_name, domain=domain, path=path
)
return
if session.accessed:
response.vary.add("Cookie")
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
app.session_cookie_name,
val,
expires=expires,
httponly=httponly,
domain=domain,
path=path,
secure=secure,
samesite=samesite,
)
闪现
flash
,等同于djagno的message框架
# 假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息
-本质其实就是在a页面,把错误信息放到了某个位置,在b页面把错误取出来
# 本质就是放到session中了
#使用:
-放值
-flash(err)
-flash(err,category='lqz')
-取值
-get_flashed_messages()
-err = get_flashed_messages(category_filter=['lqz'])
# 在一次请求中,把一些数据放在闪现中,下次请求就可以从闪现中取出来,取一次就没了(使用分类也是这样)
请求扩展
flask的请求扩展,基于装饰器,在请求来了,请求走了,出异常了,页面找不到等情况下会执行该装饰器装饰的函数
1.before_request
-return四件套,不继续往下走了,如果retrun None,继续走下一个请求扩展
-请求来了,就会执行它
-多个before_request,会从上往下依次执行
2.after_request
-必须返回response对象
-请求走了,会执行它
-多个after_request,从下往上依次执行
3.before_first_request
-项目启动后,第一次访问,会执行
-返回None,会继续往下执行,返回四件套,就不继续往后走了
4.teardown_request
-无论程序是否出异常,都会触发它
-记录错误日志
-app.debug=False模式才行
5.errorhandler
-监听某个状态码,如果是这个状态码的错误,就会触发它的执行
-可以返回四件套,统一返回格式
6.template_global:标签
# 定义
@app.template_global()
def sb(a1, a2):
return a1 + a2
# 模板中使用
{{sb(1,2)}}
7.template_filter:过滤器
# 定义
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
# 模板中使用
{{ 1|db(2,3)}}
#总结:
1 重点掌握before_request和after_request,
2 注意有多个的情况,执行顺序
3 before_request请求拦截后(也就是有return值),response所有都执行 ---》django一样
蓝图
Blue_print,作用是对目录进行划分
# 不使用蓝图来划分目录----因为全局就一个app对象,导来导去,很容易出现循环导入的问题
-templates
-views
-__init__.py
-user.py
-order.py
-app.py
# 蓝图的使用步骤:
1 实例化得到蓝图对象 ---可以传一些参数:account = Blueprint('account', __name__,templates,static)
2 把蓝图对象在app中注册----app.register_blueprint(account,制定前缀)
3 使用蓝图对象,注册路由
4 蓝图有自己的请求扩展----app对象总的请求扩展,每个蓝图有自己的请求扩展
# 使用蓝图来做目录划分---》使用蓝图对象,取代app对象
-小型项目:目录结构如下,只有一个app的项目
-flask_pro #项目名
-pro_flask # 文件夹
-__init__.py # 包的init文件
-static #静态文件
-code.png
-templates #模板文件
-index.html
-views #视图函数写在里面
-account.py #订单相关视图函数
-user.py #用户相关视图函数
-blog.py #博客相关视图函数
-manage.py # 项目的启动文件
-大型项目:目录结构如下,多个app的项目
使用蓝图的小型项目
-flask_pro #项目名
-pro_flask # 文件夹
-__init__.py # 包的init文件
-static #静态文件
-code.png
-templates #模板文件
-index.html
-views #视图函数写在里面
-account.py #订单相关视图函数
-blog.py #博客相关视图函数
-manage.py # 项目的启动文件
pro_flask/init.py
from flask import Flask
app = Flask(__name__,template_folder='templates',static_folder='statics',static_url_path='/static')
from .views.account import account
from .views.blog import blog
# 注册3个蓝图,把他们注册进app中
app.register_blueprint(account)
app.register_blueprint(blog)
pro_flask/views/account.py
from flask import Blueprint
from flask import render_template
# 实例化得到蓝图对象
account = Blueprint('account', __name__)
@account.route('/login.html', methods=['GET', "POST"])
def login():
return render_template('login.html')
pro_flask/views/blog.py
from flask import Blueprint, render_template,request
blog = Blueprint('blog', __name__)
@blog.route('/get_blog')
def get_blog():
print(request.path)
return render_template('blog.html')
manage.py
from pro_flask import app
if __name__ == '__main__':
app.run()
使用蓝图的大型项目
pro_flask # 项目名
-pro_flask #包名
-admin #admin app的名字
-static #app自己的静态文件
-templates #app自己的模板文件
-__init__.py #包的init
-views.py #app自己的视图函数
-web #web app的名字
-static #app自己的静态文件
-templates #app自己的模板文件
-__init__.py #包的init
-views.py #app自己的视图函数
-__init__.py
-run.py
pro_flask/admin/init.py
from flask import Blueprint
# 第一步:初始化蓝图
admin = Blueprint(
'admin',
__name__,
template_folder='templates', # 指定该蓝图对象自己的模板文件路径
static_folder='static' # 指定该蓝图对象自己的静态文件路径
)
from . import views
pro_flask/admin/views.py
from . import admin
from flask import render_template
@admin.before_request
def before():
print('admin 的 before')
# 第三步:使用蓝图注册路由
@admin.route('/index')
def index():
return render_template('admin.html')
pro_flask/web/init.py
from flask import Blueprint
web = Blueprint(
'web',
__name__,
template_folder='templates',
static_folder='static'
)
from . import views
pro_flask/web/views.py
from . import web
@web.route('/index')
def index():
return 'Web.Index'
pro_flask/init.py
from flask import Flask
from .admin import admin
from .web import web
app = Flask(__name__)
app.debug = True
# 第二步:在app中注册蓝图
app.register_blueprint(admin, url_prefix='/admin') # url_prefix='/admin' 访问这个app的前缀,等同于include
app.register_blueprint(web)
run.py
from pro_flask import app
if __name__ == '__main__':
app.run()
g对象
专门用来存储用户信息的g对象,g的全称的为global
g对象在一次请求中的所有的代码的地方,都是可以使用的
为什么不放在request中,是为了防止值被覆盖掉
# g和session的区别?
session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次
from flask import Flask, g, request,session
app = Flask(__name__)
app.debug=True
@app.before_request
def before():
print(type(g)) # werkzeug.local.LocalProxy 代理模式
print(type(session))
print(type(request))
if request.path == '/':
# request.method='lqz'
g.name = 'lqz' # 放到g对象中
else:
# request.name = 'pyy'
g.name = 'pyy'
def add(a,b):
print(g.name)
# print('-----', request.name)
@app.route('/')
def index():
print(g.name)
# print('-----',request.name)
# add(1,2)
return 'hello'
@app.route('/home')
def home():
print(g.name)
return 'hello'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
flask-session的使用
# session默认以cookie的形式放到浏览中,我们想把session,放到服务端保存(redis中,mysql中。。。)
# 只需要写一个类,写open_session和save_session方法
from flask import Flask, g, request,session
app = Flask(__name__)
app.debug=True
app.secret_key='asdasdfasdf'
# 使用flask-session:方式一:
from flask_session import RedisSessionInterface
app.session_interface=RedisSessionInterface(redis=None,key_prefix='luffy_')
#方式二:通用方案
from redis import Redis
from flask_session import Session
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_KEY_PREFIX'] = 'luffy'
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1',port='6379')
Session(app) #后面会经常见这种写法--->类(app)---》包一下的写法
@app.route('/set_session')
def set_session():
session['name']='lqz'
return '设置成功'
@app.route('/get_session')
def get_session():
print(session['name'])
return '获取成功'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
数据库连接池
flask可以使用pymysql操作数据库
多个线程使用同一个链接对象,会导致数据错乱
每个线程使用一个连接,会导致mysql连接数过大
第三方模块
所以借助于dbutils模块来实现数据库连接池
pip3 install dbutils
# https://www.cnblogs.com/liuqingzheng/articles/9006055.html
pool.py
from dbutils.pooled_db import PooledDB
import pymysql
POOL=PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。
ping=0,
# ping MySQL服务端,检查是否服务可用。
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='luffy_api',
charset='utf8'
)
flask测试
from flask import Flask
# import pymysql
from pool import POOL
import pymysql
app = Flask(__name__)
# 定义在外面,全局conn,cursor,会出现数据错乱,正常情况应该放在视图函数内部建立链接,但是会影响性能,所有需要建立数据库连接池
# conn = pymysql.connect(host='127.0.0.1', port=3306, database='luffy_api', user='root', password='123')
# cursor = conn.cursor()
@app.route('/banner')
def banner():
# 从池中拿链接,创建出cursor对象
# conn = POOL.connection()
# cursor = conn.cursor()
conn = pymysql.connect(host='127.0.0.1', port=3306, database='luffy_api', user='root', password='123')
cursor = conn.cursor()
cursor.execute('select * from luffy_banner')
print(cursor.fetchall())
return 'hello'
@app.route('/order')
def order():
# 从池中拿链接,创建出cursor对象
# conn = POOL.connection()
# cursor = conn.cursor()
conn = pymysql.connect(host='127.0.0.1', port=3306, database='luffy_api', user='root', password='123')
cursor = conn.cursor()
cursor.execute('select * from luffy_order')
print(cursor.fetchall())
return 'hello'
if __name__ == '__main__':
app.run()
压力测试
import requests
from threading import Thread
def task():
res = requests.get('http://127.0.0.1:5000/banner')
print(res.text)
res = requests.get('http://127.0.0.1:5000/order')
print(res.text)
# 查询目前有多少个mysql的客户端链接着mysql
if __name__ == '__main__':
for i in range(10000):
t = Thread(target=task)
t.start()
# 查看当前有多个个连接数
show status like 'Threads%'
wtfroms(了解)
在前后端混合的项目中
django中使用forms:校验数据,渲染页面
flask中使用第三方wtfroms完成forms的功能
# 安装
pip3 install wtforms
py文件
from flask import Flask,request,render_template
app = Flask(__name__)
from wtforms.fields import core
from wtforms import Form
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
class LoginForm(Form):
# 字段(内部包含正则表达式)
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空.'),
validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(), # 页面上显示的插件
render_kw={'class': 'form-control'}
)
# 字段(内部包含正则表达式)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.'),
validators.Length(min=8, message='用户名长度必须大于%(min)d'),
validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
class RegisterForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired()
],
widget=widgets.TextInput(),
render_kw={'class': 'form-control'},
default='alex'
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
pwd_confirm = simple.PasswordField(
label='重复密码',
validators=[
validators.DataRequired(message='重复密码不能为空.'),
validators.EqualTo('pwd', message="两次密码输入不一致")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
gender = core.RadioField(
label='性别',
choices=(
(1, '男'),
(2, '女'),
),
coerce=int # “1” “2”
)
city = core.SelectField(
label='城市',
choices=(
('bj', '北京'),
('sh', '上海'),
)
)
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
favor = core.SelectMultipleField(
label='喜好',
choices=(
(1, '篮球'),
(2, '足球'),
),
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput(),
coerce=int,
default=[1, 2]
)
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
def validate_pwd_confirm(self, field):
"""
自定义pwd_confirm字段规则,例:与pwd字段是否一致
:param field:
:return:
"""
# 最开始初始化时,self.data中已经有所有的值
if field.data != self.data['pwd']:
# raise validators.ValidationError("密码不一致") # 继续后续验证
raise validators.StopValidation("密码不一致") # 不再继续后续验证
@app.route('/login',methods=['GET','POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('login.html', form=form)
else:
form = LoginForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('login.html', form=form)
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
form = RegisterForm(data={'gender': 2,'hobby':[1,]}) # initial
return render_template('register.html', form=form)
else:
form = RegisterForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run()
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post">
<p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
<p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户注册</h1>
<form method="post" novalidate style="padding:0 50px">
{% for field in form %}
<p>{{field.label}}: {{field}} {{field.errors[0] }}</p>
{% endfor %}
<input type="submit" value="提交">
</form>
</body>
</html>
信号
signal
# 某种情况下会触发某个函数执行----》aop理念,面向切面编程的理念
-装饰器实现
-魔法方法实现
-其他方式
# 链式调用:编程的思维方法(java,js中使用很多),queryset对象
-如何实现,方法执行结果返回当前对象
-对象.方法().方法().方法()
内置信号
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
from flask import Flask, render_template
from flask import signals
app = Flask(__name__)
# 内置信号使用步骤
# 第一步:写个函数
def render_before(*args,**kwargs):
print(args)
print(kwargs)
print('模板要渲染了')
# 第二步:跟信号绑定
signals.before_render_template.connect(render_before)
# 第三步:触发信号 (内置信号,会自动触发)
@app.route('/')
def index():
print('index 执行了')
return render_template('index.html', name='刘亦菲')
if __name__ == '__main__':
app.run()
'''使用步骤
# 第一步:写个函数
# 第二步:跟信号绑定
# 第二步:跟信号绑定
'''
自定义信号
from flask import Flask, render_template,request
from flask import signals
from flask.signals import _signals
app = Flask(__name__)
# 第一步:定义信号
xxx = _signals.signal('xxx')
# 第二步:写个函数
def add(*args, **kwargs):
print(args)
print(kwargs)
print('add执行了')
# 第三步:跟信号绑定
xxx.connect(add)
#第四步:触发信号
@app.route('/')
def index():
xxx.send(request=request)
print('index 执行了')
return render_template('index.html', name='刘亦菲')
if __name__ == '__main__':
app.run()
'''
# 第一步:定义信号
# 第二步:写个函数
# 第三步:跟信号绑定
#第四步:触发信号
'''
多app应用
# 一个flask项目,支持多个app对象的
from flask import Flask
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.serving import run_simple
app1 = Flask('app01')
app2 = Flask('app02')
@app1.route('/index')
def index():
return "app01"
@app2.route('/index2')
def index2():
return "app2"
dm = DispatcherMiddleware(app1, {
'/sec': app2,
})
if __name__ == "__main__":
# 请求来了,会执行 dm()---->触发DispatcherMiddleware的__call__
run_simple('localhost', 5000, dm)
flask-script
# djagno中执行djagno程序,通过命令:python3 manage.py runserver--->还有其他很多命令
# 想让flask能够执行python3 manage.py runserver启动flask项目,自定制一些命令,完成更多操作
# django中自定制命令?
# 借助于第三方 flask-script来完成上面的需求
# pip3 install flask-script
from flask import Flask
# 使用步骤
from flask_script import Manager
app = Flask('app01')
#基本使用,多了runserver命令
manager=Manager(app)
# 高级使用:自定制命令 python manage.py dbinit
@manager.command
def dbinit():
"""
python manage.py dbinit
"""
print("数据库初始化完成")
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令(-n也可以写成--name)
执行: python manage.py cmd -n lqz -u xxx
执行: python manage.py cmd --name lqz --url yyy
"""
print(name, url)
# 编一个功能,通过execl---》把execl的数据存同步到某个数据表中得命令
@app.route('/index')
def index():
return "app01"
if __name__ == "__main__":
# app.run()
manager.run()
flask请求上下文分析
# request,session 是全局的,每个视图函数都在用,为什么不乱
# 每次请求来了,都是一个线程或者协程执行这个app()
# 请求扩展,在源码中得哪些位置执行的?
# 内置信号的触发,在哪触发的
# 请求来了,会执行Flask类的__call__----> app(environ, start_response)---->self.wsgi_app(environ, start_response)---->
# Flask类的方法wsgi_app,这是整个flask请求的声明周期
def wsgi_app(self, environ, start_response):
# 把environ转成Requet类的对象,当次请求对象,放到了ctx中
ctx = self.request_context(environ)
error = None
try:
try:
# 干了非常牛逼的事
ctx.push()
# 执行路由匹配,执行视图函数
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
# 返回了响应对象
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?