首页 > 其他分享 >Flask 框架学习笔记

Flask 框架学习笔记

时间:2024-04-18 09:13:39浏览次数:25  
标签:__ index return name 框架 Flask app 笔记 flask

Flask 介绍和安装

python web框架
同步框架:

  • Django:大而全(3.x以后支持异步)
  • flask:小而精,插件的支持
    异步框架:Sanic、FastAPI

flask介绍

Flask是一个基于Python开发并且依赖jinja2模板(DTL)和Werkzeug WSGI(符合wsgi协议的web服务器,wsgiref)服务的一个微型框架,对于Werkzeug本质是Socket服务端,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理,即:将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。

flask安装

pip install flask

简单使用

当PyCharm 中创建 flask 这些会自动给你配置好

from flask import Flask
# 名字随意
app = Flask('main')
# 路由
@app.route('/')
def index():
    return 'hello world'
if __name__ == '__main__':
    # 运行
    app.run()

访问:
http://127.0.0.1:5000

请求与响应

请求

flask中获取请求的数据都是通过全局的模块:request。虽然是全局,但是用起来确实各自独立的。
from flask import request

      请求方法         |                           作用                        
 request.method    |                        请求方式                     
   request.args      |                     get 请求参数                   
   request.form      |                    post 请求参数                  
  request.values     |             get,post请求的参数总和          
   request.files       |                           文件                        
 request.cookies    |                      请求cookies                   
request.headers    |                          请求头                       
   request.path      |   服务器地址后面的路径(不带get请求参数)
request.full_path   |     服务器地址后面的路径(带get请求参数)  
    request.url        |      带服务器地址的路径(带get请求参数)   
request.base_url    |    带服务器地址的路径(不带get请求参数)  
 request.url_root    |                  服务器IP地址:端口               
request.host_url    |                  服务器IP地址:端口               
   request.host      |           服务器IP地址:端口(不带http)         

响应

返回字符串

类比 Django 中 HttpResponse(‘字符串’)

from flask import Flask
# 名字随意
app = Flask('main')
# 路由
@app.route('/')
def index():
    return '字符串'

if __name__ == '__main__':
    # 运行
    app.run()

返回模版(网页)

类比 Django 中 render(reqest,模版,{})

from flask import Flask, render_template
# 名字随意
app = Flask('main')
# 路由
@app.route('/')
def index():
    return render_temiplate(模板, 参数1=?, 参数2=?)

if __name__ == '__main__':
    # 运行
    app.run()

返回重定向

类比 Django 中的 redirect('url')

from flask import Flask, redirect
# 名字随意
app = Flask('main')
# 路由
@app.route('/')
def index():
    return redirect('路由')
if __name__ == '__main__':
    # 运行
    app.run()

返回 json

类比 Django 中的 JSonResponse

from flask import Flask, jsonify
# 名字随意
app = Flask('main')
# 路由
@app.route('/')
def index():
    return jsonify(字典或列表..)

if __name__ == '__main__':
    # 运行
    app.run()

响应包装

方法导入: from flask import make_response
使用:

res = make_response(render_template('home.html'))
# 写入cookie
res.set_cookie('name','lqz')
# 删除cookie
res.delete_cookie('name')
# 添加响应头
res.headers[''] = ''
# 返回
return res

案例-登陆

实现 登陆成功时保持 cookie ,没有登陆时 无法访问特定的页面
项目目录结构:

项目名
 ├── templates  -- 网页文件夹,必须叫templates
 ├    ├── detail.html -- 单用户详情页
 ├    ├── index.html -- 用户列表
 ├    └── login.html -- 登录页面
 └── login.py -- 功能实现

index.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

<!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>

login

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    用户名<input type="text" name="username">
    密码<input type="password" name="password">
    <input type="submit" value="登录">
    <p>{{ error }}</p>
</form>
</body>
</html>

login.py

from flask import Flask, render_template, redirect, jsonify, request, session
from functools import wraps

app = Flask('main')
app.secret_key = 'abcdd'  # 如果要使用session,必须写秘钥
# 用户信息,暂时用全局变量存储
USERS = {
    1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"},
    2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"},
    3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"},
}


# 登录装饰器,判断是否登录
def login_decorator(func):
    @wraps(func)  # 装饰器修复技术
    def inner(*args, **kwargs):
        username = session.get('username')
        if username:
            return func(*args, **kwargs)
        else:
            # 没有登录返回登录页面
            return redirect('/login')
    return inner

# 默认转到的页面,但是加上了装饰器,当没有登陆的时候 是不能登陆到 index 该页面
@app.route('/')
@login_decorator
def index():
    return render_template('index.html', user_dict=USERS)

# 将字典中的键传入到路由中
@app.route('/detail/<int:pk>')
@login_decorator
def detail(pk):
    return render_template('detail.html', info=USERS.get(pk))


# 只接受get和post请求
@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 == 'tom' and password == '123':
            # cookie保存
            session['username'] = 'tom'
            # 成功跳转到http://127.0.0.1:5000/
            return redirect('/')
        else:
            # 登录失败给页面传参显示错误信息
            return render_template('login.html', error='用户名或密码错误')

if __name__ == '__main__':
    # 运行
    app.run()

配置文件的写法

第一种

app.secret_key='asfasdf' # 如果要使用session,必须写秘钥
app.debug=True

第二种: 将 app 所有的配置项都在这个 app.config 中

app.config['DEBUG']=True

第三中: 仿 Django 的 settings.py 写法(导入)

app.config.from_pyfile('settings.py')
#在 settings.py中
DEBUG = False
NAME= 'lqz' 

第四种: 通过类的方法导入

app.config.from_object('settings.DevelopmentConfig')

class Config(object):
    DEBUG = False
    DATABASE_URI = 'sqlite://:memory:'

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    DEBUG = True

其它

app.config.from_envvar("环境变量名称")
app.config.from_json("json文件名称")

路由系统

路由写法

使用装饰器

@app.route('/index', endpoint='index')
def index():
    return 'hello'

使用 方法写路由

        def index():
            return 'hello'
        app.add_url_rule('/index', view_func=index)
其中
`app.add_url_rule('/index', view_func=index)` 路由有个别名,如果不写endpoint,会自动以函数名作为endpoint
endpoint相当于 Django 路由中 name 属性

** add_url_rule 参数**

    rule:请求的路径,可以使用转换器
    endpoint:别名--》反向解析
    view_func:[视图类.as_view(name='xx')]或者[视图函数内存地址]
    methods:允许的请求方式
    # ---------以下不重要-----
    defaults:字典,给视图函数传默认值
    strict_slashes:对URL最后的 / 符号是否严格要求
    redirect_to:重定向到

本质

这两种的写法的本质都是一样的,他们的无非还是加了个装饰器
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
显然,装饰器中也调用了 add_url_rule 的方法

转换器

'default':          UnicodeConverter,
'string':           UnicodeConverter,
'any':              AnyConverter,
'path':             PathConverter,
'int':              IntegerConverter,
'float':            FloatConverter,
'uuid':             UUIDConverter,   

使用

@app.route('/index/<int:pk>', endpoint='index')
def index(pk):
    print(pk)
    return 'hello'

访问

http://127.0.0.1:5000/index/11

本质 CBV

基于类的视图为 CBV
基于函数的视图为 FBV
写法 继承 MethodView, 和 Django 很想

from flask.views import MethodView
class Home(MethodView):
    # cbv只允许某个请求方式
    methods=['POST', 'GET']
    # cbv加装饰器
    # decorators = (装饰器名字,装饰器名字2)
    def get(self):
        return 'home'

    def post(self):
        return 'home-post'
# as_view(name='home'),name就是路径的别名——endpoint
app.add_url_rule('/home', view_func=Home.as_view(name='home'))

源码剖析

Home.as_view()执行时,由于Home类和父类MethodView没有as_view()方法,所以这执行的其实是MethodView类的父类View中的方法。这个方法执行时返回了如下函数:

def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
    self = view.view_class(*class_args, **class_kwargs)   # type: ignore
    return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)

这里用到了 self.dispatch_request 而这个dispatch_request用的是MethodView类中的:

def dispatch_request(self, *args, **kwargs):
    meth = getattr(self, request.method.lower(), None)

    if meth is None and request.method == "HEAD":
        meth = getattr(self, "get", None)

    assert meth is not None, f"Unimplemented method {request.method!r}"
    return current_app.ensure_sync(meth)(*args, **kwargs)

本质上与 Django 中一样 利用反射去调用类中的方法

Session 的使用和原理

在使用session 之前 必须先设置一下密钥
app.secret_key="asdas" # 值随便

使用

from flask import Flask, session
app = Flask('main')
app.secret_key = 'abcdd'  # 如果要使用session,必须写密钥

@app.route('/')
def index():
    session['name'] = 'tom'
    return 'index'

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

原理

flask在设置session时,会将session对象序列化成字符串,然后放到cookie中,保存在客户端中。
获取session时,从cookie中取出字符串反序列化,得到session对象,再获取指定值。

源码分析

app.session_interface = SecureCookieSessionInterface()

session对象就是SecureCookieSessionInterface类实例化得到的。这个类中有两个方法:
open_session:当有请求来时,从cookie中取出字符串反序列化得到session对象。

def open_session(self, app, request):
    s = self.get_signing_serializer(app)
    if s is None:
        return None
    # 从cookie中获取字符串
    val = request.cookies.get(self.get_cookie_name(app))
    if not val:
        return self.session_class()
    max_age = int(app.permanent_session_lifetime.total_seconds())
    try:
        # 把字符串反序列化得到session对象
        data = s.loads(val, max_age=max_age)
        return self.session_class(data)
    except BadSignature:
        return self.session_class()

save_session:响应时,session对象序列化成字符串,然后放到cookie中,保存在客户端中

def save_session(self, app, session, response):
    name = self.get_cookie_name(app)
    domain = self.get_cookie_domain(app)
    path = self.get_cookie_path(app)
    secure = self.get_cookie_secure(app)
    samesite = self.get_cookie_samesite(app)
    
    # 如果session为空,直接返回,不设置cookie了
    if not session:
        # 如果session被修改为空,删除cookie
        if session.modified:
            response.delete_cookie(
                name, domain=domain, path=path, secure=secure, samesite=samesite
            )
        return
    if session.accessed:
        response.vary.add("Cookie")
    if not self.should_set_cookie(app, session):
        return
    httponly = self.get_cookie_httponly(app)
    # 过期时间
    expires = self.get_expiration_time(app, session)
    # 把session对象序列化成字符串
    val = self.get_signing_serializer(app).dumps(dict(session))
    # 设置到cookie中
    response.set_cookie(
        name,
        val,
        expires=expires,
        httponly=httponly,
        domain=domain,
        path=path,
        secure=secure,
        samesite=samesite,
    )

flask- session 的使用

session默认以cookie的形式放到客户端中,如果想把session放到服务端保存(redis、mysql...),需要写一个类,重写open_session和save_session方法。而flask-session就已经帮我们做好了。
安装: pip install flask-session
存到服务端 就不需要写密钥了

方式一

from flask import Flask, session
from flask_session import RedisSessionInterface
# 从 flak_session 导入这个模块 可能会报错 没有这个包
那么就使用源文件中的 from redis import RedisSession, RedisSessionInterface  # noqa: F401
记住 最后的注释 #noqa:F401 不能删去 会报错

app = Flask('main')
app.session_interface = RedisSessionInterface(redis=None, key_prefix='index_')
"""
redis:Redis对象,为None默认为Redis()
key_prefix:存到redis中键名的前缀
"""

@app.route('/')
def index():
    session['name'] = 'tom'
    return 'index'

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

方式二: 通用方案

from flask import Flask, session
from redis import Redis
from flask_session import Session

app = Flask('main')

app.config['SESSION_TYPE'] = 'redis'  # session存到哪里
app.config['SESSION_KEY_PREFIX'] = 'index_'  # 存到redis中键名的前缀
app.config['SESSION_REDIS'] = Redis(host='127.0.0.1', port='6379')  # Redis对象
Session(app)

@app.route('/')
def index():
    session['name'] = 'tom'
    return 'index'

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

闪现 Flash

假设在a页面操作出错,跳转到b页面,想要在b页面显示a页面的错误信息,这时候就可以使用flash。
在一次请求中,把一些数据放在flash中,下次请求就可以从flash中取出来,取一次就没了。
本质其实就是放到session中。

放值

from flask import flash
# 普通
flash(msg)
# 分类
flash(msg, category='err')

取值

from flask import get_flashed_messages
# 普通取值
msg = get_flashed_messages()
# 分类取值
msg = get_flashed_messages(category_filter=['err'])

无论何种取值方式,去一次就没了 但是一个请求可以多次取值

举例

from flask import Flask, flash, get_flashed_messages,jsonify

app = Flask('main')
app.secret_key = 'abcdd' # 本质是使用session,所以需要密钥

@app.route('/')
def index():
    flash('from index')
    flash('from index xx', category='xx')
    return 'index'

@app.route('/home')
def home():
    msg_xx = get_flashed_messages(category_filter='xx')
    return jsonify(msg_xx)

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

请求扩展

请求扩展是使用装饰器装饰函数,让程序执行时某个时候会执行被装饰的函数。

1.before_request:有请求来时,会执行,如果有多个,从上往下依次执行。

如果被装饰的函数return None,继续走下一个请求扩展;如果return的是响应对象,直接返回,不继续往下走了

@app.before_request
def expand():
    print('请求来了')

@app.route('/')
def index():
    print('index执行')
    return 'index'

2.after_request:请求走了,会执行,如果有多个,从下往上依次执行。

必须返回响应对象

@app.after_request
def expand(response):
    print('请求走了')
    return response

@app.route('/')
def index():
    print('index执行')
    return 'index'

3.before_first_request:项目启动后,第一次请求访问,会执行。

返回值不会影响程序

@app.before_first_request
def expand():
    print('第一次请求')

@app.route('/')
def index():
    print('index执行')
    return 'index'

4.teardown_request:无论程序是否出异常,都会触发它。

用于记录错误日志,app.debug = False模式才会触发

@app.teardown_request
def expand(e):
    if e:
        print('程序报错:', e)

@app.route('/')
def index():
    print('index执行')
    l = []
    print(l[0])
    return 'index'

5.errorhandler:监听某个状态码,如果是这个状态码的错误,就会触发它的执行。

如果被装饰的函数return None,继续走下一个请求扩展;如果return的是响应对象,直接返回,不继续往下走了。

@app.errorhandler(404)
def expand(e):
    print(e)
    return '页面不存在'

6.template_global:标签,用于模板

@app.template_global()
def add(a1, a2):
    return a1 + a2
# {{ add(1,2) }}

7.template_filter:过滤器,用于模板

@app.template_filter()
def add(a1, a2, a3):
    return a1 + a2 + a3
# {{ 1|add(2,3) }}

G 对象

flask中有个 g 对象,在一次请求中,可以赋值,取值。只对当次请求有效。用后则弃

from flask import Flask, g

app = Flask('main')

@app.before_request
def expand():
    g.a1 = 'asd'
    g.a2 = 'bbb'
def add():
    return g.a1 + g.a2
@app.route('/')
def index():
    return add()

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

request也可以实现一样的功能,但是并不推荐,因为设置值时可能把一些已有的值覆盖。

蓝图

目前使用flask时,都是在一个py文件中编写,如果代码过多,就很不方便,所以可以使用蓝图来划分目录。
蓝图的使用步骤:

1.实例化得到蓝图对象,可以传一些参数

index = Blueprint(
    'index', 
    __name__,
    templates,
    static
)

2.把蓝图对象在app中注册

app.register_blueprint(index, 路由前缀)

3.使用蓝图对象,注册路由

@index.route('/')
def get_index():
    return ''

4.蓝图有自己的请求扩展,每个蓝图有自己的请求扩展

项目名
├── 项目包
├    ├── static -- 存放静态文件(js、css、img..)
├    ├── templates -- 存放模板文件(html)
├    ├    └── index.html -- index页面
├    ├── views -- 存放视图的文件夹
├    ├    └── index.py -- index相关视图
├    └── __init__.py -- 包的init文件
└── manage.py -- 项目的启动文件

init.py

from flask import Flask
app = Flask(
    __name__,
    template_folder='templates',  # 模板文件的文件夹名称,不写也行,默认就是这个值
    static_folder='static',  # 静态文件的文件夹名称,不写也行,默认就是这个值
    static_url_path='/static'  # 静态文件路径
)

from .views.index import index

# 注册蓝图,注册进app中
app.register_blueprint(index)

index.py

from flask import Blueprint
from flask import render_template
# 实例化得到蓝图对象
index = Blueprint('index', __name__)

# 通过蓝图对象编写路由
@index.route('/get_index')
def get_index():
    return render_template('index.html')

manage.py

# 导入__init__.py中的app对象
from 项目包 import app

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

html 文件引入静态文件

/static/..

大型蓝图

项目名
├── 项目包
├    ├── index -- 一个应用一个包
├        ├── static -- 存放静态文件(js、css、img..)
├        ├── templates -- 存放模板文件(html)
├        ├    └── index.html -- index页面
├        ├── views.py -- 应用自己的视图函数
├        └── __init__.py -- 包的init文件
├    ├── home -- 一个应用一个包
├        ├── static -- 存放静态文件(js、css、img..)
├        ├── templates -- 存放模板文件(html)
├        ├    └── home.html -- home页面
├        ├── views.py -- 应用自己的视图函数
├        └── __init__.py -- 包的init文件
├    └── __init__.py -- 项目包的init文件
└── manage.py -- 项目的启动文件

index应用包下的__init__.py

from flask import Blueprint

# 初始化蓝图
index = Blueprint(
    'index',
    __name__,
    template_folder='templates',  # 指定该蓝图对象自己的模板文件路径
    static_folder='static'       # 指定该蓝图对象自己的静态文件路径
)
# 需要放在下面导入
from . import views

index应用包下的views.py

from . import index
from flask import render_template

# 使用蓝图注册路由
@index.route('/get_index')
def get_index():
    return render_template('index.html')

项目包下的__init__.py

from flask import Flask
from .index import index

app = Flask(__name__)
app.debug = True

# 在app中注册蓝图
app.register_blueprint(index, url_prefix='/index')  # 访问这个app的前缀,加上蓝图的路由,等同于include,/index/get_index

manage.py

# 导入__init__.py中的app对象
from 项目包 import app

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

html 文件引入静态文件

/index/static/..

数据库连接池

多个线程使用同一个连接对象,会导致数据错乱;每个线程使用一个连接,又会导致mysql连接数过大。
所以需要使用数据库连接池,让线程去连接池中拿连接,固定了连接数,也不会导致数据错乱。
借助于第三方模块,实现数据库连接池:
新建py文件 pool.py存放连接池对象

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不限制
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    maxusage=None,  # 一个连接最多被重复使用的次数,None表示无限制
    setsession=[],  # 开始会话前执行的命令列表。
    ping=0,
    # ping MySQL服务端,检查是否服务可用。
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',  # 密码
    database='db_name',  # 数据库名称
    charset='utf8'  # 编码
)

flask

from flask import Flask
from pool import POOL

app = Flask(__name__)

@app.route('/index')
def index():
    # 从池中拿链接,创建出cursor对象
    conn = POOL.connection()
    cursor = conn.cursor()
    # 数据库查询
    cursor.execute('select * from table_name1 ')
    print(cursor.fetchall())
    return 'hello'

@app.route('/home')
def home():
    # 从池中拿链接,创建出cursor对象
    conn = POOL.connection()
    cursor = conn.cursor()
    # 数据库查询
    cursor.execute('select * from table_name2')
    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/index')
    print(res.text)
    res = requests.get('http://127.0.0.1:5000/home')
    print(res.text)

if __name__ == '__main__':
    for i in range(10000):
        t = Thread(target=task)
        t.start()
"""
查看当前有多个个连接数   show status like 'Threads%'
"""

Wtforms

wtforms类似于django前后端混合项目中使用的forms组件。

flask

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>

信号

信号: 某种情况下 会触发某个函数的执行
flask中帮我们集成了信号的使用,但还是需要我们安装一个模块。
安装:pip install blinker

内置信号

from flask import signals

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

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

自定义信号

from flask import Flask
from flask.signals import _signals

app = Flask(__name__)

# 第一步:定义信号
xxx = _signals.signal('xxx')

# 第二步:写个函数
def add(args):
    print(args)
    print('add执行了')

# 第三步:跟信号绑定
xxx.connect(add)

@app.route('/')
def index():
    # 第四步:触发信号
    xxx.send()
    print('index 执行了')
    return 'index'

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

flask-script

djagno中执行djagno程序,通过命令:python manage.py runserver。
如果想让flask也能通过执行python manage.py runserver启动flask项目,或者一些其他的命令完成其他操作。
安装:pip install flask-script

基本使用:

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@app.route('/')
def index():
    return 'index'

if __name__ == '__main__':
    # 需要run一下
    manager.run()

终端输入命令:

python py文件名 runserver

自定义命令:

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@manager.command
def init():
    print("初始化")

if __name__ == '__main__':
    # 需要run一下
    manager.run()

自定义命令

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@manager.command
def init():
    print("初始化")

if __name__ == '__main__':
    # 需要run一下
    manager.run()

终端输入命令

python py文件名 init

自定义高级命令

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
    """
    自定义命令(-n也可以写成--name)
    执行: python manage.py  cmd -n tom -u xxx
    执行: python manage.py  cmd --name tony --url abc
    """
    print(name, url)

if __name__ == '__main__':
    # 需要run一下
    manager.run()

标签:__,index,return,name,框架,Flask,app,笔记,flask
From: https://www.cnblogs.com/yiermu/p/18142206

相关文章

  • 树链剖分lca笔记
    树链剖分求lca参考资料:https://www.cnblogs.com/cangT-Tlan/p/8846408.html[树链剖分lca]大佬讲的很清楚%%%orz这篇博客是我的学习笔记。树链剖分:树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结......
  • Python量化交易系统实战_学习笔记(更新中)
    作者:麦克煎蛋  出处:https://www.cnblogs.com/mazhiyong/转载请保留这段声明,谢谢!此系列的文章主要是基于慕课网的课程做的学习笔记,算是量化交易的入门级课程。这个系列的课程,好处是通俗易懂,适合刚上手的小白。但感觉数据部分限制见多,后面再更新下对于数据源的文章吧。 课......
  • [学习笔记] 高斯消元 - 线性代数
    高斯-约旦消元下面给两道板子【模板】高斯消元法最最基础的板子,没啥哆嗦的。下面给出高斯-约旦消元解法。#include<bits/stdc++.h>usingnamespacestd;intn,dt=1;doubleeps=1e-9,m[102][102];intmain(){ scanf("%d",&n); for(inti=1;i<=n;++i) for(intj......
  • 后缀数组学习笔记
    定义后缀数组是什么?(下文用\(Suf_S[i]\)表示\(S[i,i+1,\cdots,|S|]\),对\(Suf_T\)同理。并用\(S[l,r]\)表示\(S[l,l+1,\cdots,r]\),对\(T[l,r]\)同理)后缀数组包含两个数组\(rk,sa\)。\(rk[i]\)表示后缀\(Suf_S[i]\)排序后的排名。\(sa[i]\)表示排......
  • OP-TEE学习笔记(一)
    OP-TEE由于工作原因接触过一些使用TEE代替HSM来实现密钥存储、密钥对生成的方案,因此想了解一下开源的TEE方案与TEE供应商提供的方案有什么不同。关于OP-TEEOP-TEE是设计为与在Arm上运行的非安全Linux内核配合使用的一种受信任执行环境(TEE),Cortex-A内核使用TrustZone技术。OP......
  • JVM学习笔记
    1.运行时数据区1.2运行时数据区及线程概述JVM将内存划分为俩种类型的数据区域线程共享:JVM启动时创建,退出时才销毁线程私有:线程创建时创建,线程退出时销毁1.2.1运行时数据区JVM内存布局规定了Java运行过程中内存申请,分配,管理的策略,保证高效运行。不同JVM在内存划分和管理......
  • C# 闲来笔记
    1.Debug.WriteLine、Trace.WriteLine//WriteLineWritePrintDebug.WriteLine("debug调试模式下--在跟踪窗口--输出指定内容:",message);//Trace.WriteTrace.WriteLine("debugreplease模式--在跟踪窗口--下输出指定内容",message);Debug.write()用于调试模式下在输出窗口,输出调......
  • mysql_笔记
    MySQL安装与连接安装MySQL官网下载MySQL选择社区免费版下载安装选择.msi安装包双击安装,安装过程可以无脑下一步MySQL启动/关闭开始菜单搜索cmd,找到命令提示符,然后使用管理员身份打开输入命令开启:netstartmysql80关闭:netstopmysql80注:命令中的mysql80取决于......
  • 【笔记】RedmiBookPro15锐龙板(7840hs)安装ubuntu2204注意事项
    /** 2024-04-17 12:53:52*/1、不要安装ubuntu2004,驱动问题很烦入,尤其是AMD的显卡驱动,不论哪个版本都不要打AMD的官方驱动,经常花屏,卡的完全不能操作,自带的开源驱动就行了,偶尔出现一两道花屏的,不影响使用,而且一会就消失了。如果经常出现在bios里调大显存试试,默认512估计不够,我......
  • 点分树(动态点分治)学习笔记
    1.定义在点分治的基础上加以变化,构造一颗支持快速修改的重构树,称之为点分树2.算法2.1.思路点分治的核心在于通过树的重心来划分联通块,减少合并层数,从而降低时间复杂度所以,我们可以按分治递归的顺序提出一颗树,易知树高至多为logn具体的说,对于每一个找到的重心,将上一次分治......