Flask入门

Flask介绍和安装

Django大而全(3.x以后支持异步),Flask:小而精,插件的支持

异步框架:SanicFastAPI

python异步的orm框架:aiomysqlaioredis

# 介绍
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有区别,但是本质其实是一样的,sanicfastapi就是这种路由方式

路由典型写法

@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)

image.png

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操作数据库

多个线程使用同一个链接对象,会导致数据错乱

image.png

每个线程使用一个连接,会导致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)
posted @   Rain_Kz  阅读(78)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示