Flask
本文将简单介绍一下基础功能
简介
Flash 是Python上的一个Web应用程序框架。
Web应用程序框架是一个库和模块的集合,使Web应用程序开发人员能够编写Web应用程序,而不必担心协议,线程管理等低级细节。
Flask通常被称为微框架,它旨在保持应用程序的核心简单且可扩展。Flask没有用于数据库处理的内置抽象层,也没有形成验证支持。相反,Flask支持扩展以向应用程序添加此类功能。
Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。
Werkzeug
它是一个WSGI工具包,它实现了请求,响应对象和实用函数。这使得能够在其上构建web框架。Flask框架使用Werkzeug作为其基础之一。
Jinja2
Jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。
安装
pip3 install flask
安装flask时会自动安装依赖项Werkzeug 和 Jinja2
项目结构
--项目名
|---static (静态)
|---templates (模板)
|---app.py (运行|启动)
Flask程序
# 从flask中导入Flask,注意,flask是包名,Flask是模块名
from flask import Flask
# 初始化Flask
app=FLask(__name__)
# route是路由的意思,这里理解成路径,设置/则默认打开index页面,注意用单引号或者双引号包含起来
app.route('/') #路由
# 定义index函数,index函数名可以换成其他的,不一定强制要index,这里也就是相当于写一个页面
def index(): #视图函数
# return的内容,会被显示在页面中
return 'hello word'
if __name__ == '__main__':
# 默认使用本地地址和5000端口,根据需求可自行修改
app.run()
app.run参数详解
app.run(host,port,debug,load_dotenv)
app.run(host='127.0.0.1',port='8080',debug=True)
host: 如果不指定IP地址,默认只能本机访问,如果需要其他地方也能访问到本机开启的flask,则需要将地址换成0.0.0.0。
port: Web服务器的端口,一个端口号对应一个程序,默认值为5000;
debug: 布尔类型,默认off状态,off状态下代码发生改变不会自动加载,适用于生产环境(production)。设为debug=True,则成为on状态,开启调试模式,只要代码改变,服务器会重新加载最新的代码,适用于开发环境(development)。
load_dotenv: 加载最近的文件.env和文件.flaskenv,用于设置环境变量的文件,也会改变工作环境,目录到包含找到的第一个文件的目录,默认为True。
debug
flask编写的程序和php不一样,每一次变动都需要重启服务器来执行变更,就显得很麻烦,为了应对这种问题,flask中的debug模式可以在不影响服务器运行下,执行更新每一次的变更。
设置
debug=True
或
app.debug=True
只需要在app.run原基础上加上debug.True,或者直接app.debug=True。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,world'
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
# app.run('127.0.0.1','8080',debug=Ture)
app.config
环境
production #生产环境
development #开发环境
testing #测试环境
配置文件
print(app.config)
<Config {'ENV': 'production', 'DEBUG': False, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': datetime.timedelta(days=31), 'USE_X_SENDFILE': False, 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': None, 'TRAP_BAD_REQUEST_ERRORS': None, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': None, 'JSON_SORT_KEYS': None, 'JSONIFY_PRETTYPRINT_REGULAR': None, 'JSONIFY_MIMETYPE': None>
配置详解
{
'DEBUG': False, # 是否开启Debug模式
'TESTING': False, # 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 默认情况下,如果应用工作在调试模式, 请求上下文不会在异常时出栈来允许调试器内省。 这可以通过这个键来禁用。 你同样可以用这个设定来强制启用它, 即使没有调试执行,这对调试生产应用很有用 (但风险也很大)
'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它
'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天
'USE_X_SENDFILE': False, # 是否弃用 x_sendfile
'LOGGER_NAME': None, # 日志记录器的名称
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None, # 服务访问域名
'APPLICATION_ROOT': None, # 项目的完整路径
'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字
'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中
'SESSION_COOKIE_PATH': None, # cookies的路径
'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志,
'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志
'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新
'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限
'TRAP_BAD_REQUEST_ERRORS': False,
# 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
# 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
'TRAP_HTTP_EXCEPTIONS': False,
# Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
# 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
# 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
# 如果这个值被设置为 True ,你只会得到常规的回溯。
'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
'JSON_AS_ASCII': True,
# 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
# Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
# 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
'JSON_SORT_KEYS': True,
#默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
# 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
# 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
}
}
修改配置文件方法
方法一(不推荐),因为只能配置两项,debug和secret_key
app.debug=True #默认false,自动重启
方法二 字典的形式
app.config["DEBUG"]=True
方法三 以文件的形式,在from_pyfile里传递路径
app.config.from_pyfile('settings.py')
或
import settings
app.config.from_object('settings')
settings.py
#配置文件
DEBUG=True
ENV='development'
方法四 以类的形式,一个文件多个套配置,减少测试与更改(推荐使用)
app.config.from_object("setobj.settings") #系统环境需能找到的模块路径,默认在app同级目录下
app方法
app.run \\启动
app.debug \\调试
app.config \\配置
app.url_map \\路由规则表
app.template_folder \\模板文件夹
route
简介
程序中见到的@app.route('/'),相当于一个路径,可以是app.route('/index'),或者其他自定义的route,设置后,在url后面加上/index就可以访问了,每一个route后面都必须会有一个def函数跟着,代码解析一下。
基于底层函数add_url_rule实现的绑定
@app.route('/index')
def index():
return 'hello,index'
同
def user():
return 'user'
app.add_url_rule('/user',view_func=user)
具体案例
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,Word'
@app.route('/setting')
def setting():
return 'hello,setting'
def user():
return 'user'
app.add_url_rule('/user',view_func=user)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
访问127.0.0.1:8080/ 返回Hello,Word
访问127.0.0.1:8080/setting 返回hello,setting
访问127.0.0.1:8080/user 返回user
实际上,flask的route在php中相当于文件名,而def中的内容,相当于文件内容
路由的变量规则
给url添加可以传入变量的地方,只需要在route中的路径后面添加标记<value_name>,然后使用def接收。代码如下:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,Word'
@app.route('/user/<username>')
def user(username):
return 'username:{0}'.format(username)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
或
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,Word'
data={'a':'北京','b':'南京','c':'上海'}
@app.route('/get/<value_name>') #name是一个变量名,默认是字符串类型
def get(value_name):
return '您所访问的城市:{0}'.format(data.get(value_name))
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
路由的多个变量
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello,Word'
@app.route('/user/<username>/<int:age>')
def user(username,age):
return 'username:{0}</br> age:{1}'.format(username,age)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
转换器类型:
<int:value_name> \\传入的参数为整型
<float:value_name> \\传入的参数为浮点类型
<string:value_name> \\传入的参数为字符串
<path:value_name> \\传入的参数为路径
uuid:UUID类型,UUID是由一组32位数的16进制所构成
<uuid:value_name> \\必须传递uuid的格式
any:备选值中的任何一个
<any(python,java):value_name> \\访问的值符合备选值即可成功访问
唯一的URL/重定向行为
以下两条规则的不同之处在于是否使用尾部的斜杠:
@app.route('/data/')
def data():
return 'The data page'
@app.route('/index')
def index():
return 'The index page'
带有斜杠的时候,看起来就如同一个文件夹。访问没有斜杠结尾的URL时(/data),Flask会自动进行重定向,帮您在尾部加上一个斜杠(/data/)。
没有斜杠的,看起来与文件类似。如果访问这个URL时添加了尾部斜杠('/index/')就会得到一个404 "未找到" 错误。这样可以保持URL唯一,并有助于搜索引擎重复索引同一页面。
response(响应)
如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。
如果返回的是一个字典,那么调用
jsonify
创建一个响应对象。如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由
(response, status)
、(response, headers)
或者(response, status, headers)
组成。status
的值会重载状态代码,headers
是一个由额外头部值组成的列表 或字典。如果视图返回的是一个响应对象,那么就直接返回它。
如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。
字符串:
return 'hello'
字典:
return {'a':'北京','b':'南京','c':'上海'}
元组:
return 'hello word',200
响应对象:
Response(response=None, status=None, headers=None, mimetype=None, content_type=None, direct_passthrough=False)
return Response('<h1>hello response</h1>')
return Response('Hello,Word',status=321,headers={'aaa':'bbb'})
make_response()
如果想要在视图内部掌控响应对象的结果,那么可以使用 make_response() 函数。
from flask import Flask,Response,make_response,render_template_string
app = Flask(__name__)
@app.route('/')
def index():
response=make_response(render_template_string('<h1>错误</h1>'),404)
response.headers['aaa']='bbb'
return response
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
request(请求)
request主要是用于在判断时,获取当前页面的方法,如果直接打开URL,就会显示GET方法,如果使用POST,就会显示POST方法
route中,methods参数来处理不同的HTTP方法,methods要有s,并且方法用[ ] 括起来,其次就是方法要大写,不能小写,request和requests不一样,request是包含在flask中的,而requests是请求网页的,不能混淆方法要大写,否则报错咧。
例:
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/admin',methods=['GET','POST'])
def index():
if request.method =='GET':
return '现在的请求是GET方法'
elif request.method =='POST':
return '现在的请求是POST方法'
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
获取方法传入的参数
GET方法
GET方法用**request.args.get('[ 参数名 ]')**来接收从url栏中传入的参数,其中参数名是自定义的,比如定义了age,那么在url栏中只能填入age=xxxxx
requests.args.get('age',default=1,type=int) \\接收传入的参数page,默认值为1,类型为int
示例如下:
dj.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>信息登记</title>
</head>
<body>
<h1>个人信息登记</h1>
<div>
<form action="admin" method="get">
<p><input type="text" name="user" placeholder="请输入用户名"></p>
<p><input type='text' name='age' placeholder='请输入年龄'></p>
<p><input type='text' name='provinces' placeholder='请输入所在城市'></p>
<p><input type="submit" name="submit" value="提交"></p>
</form>
</div>
</body>
</html>
index.html
<html>
<head>
<title>测试</title>
</head>
<body>
<h1>Hello,{{user}}</h1>
<h2>年龄:{{age}}</h2>
<h3>省份:{{provinces}}</h3>
</body>
</html>
app.py
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/admin')
def index():
user=request.args.get('user')
age=request.args.get('age')
provinces=request.args.get('provinces')
contents={'user':user,'age':age,'provinces':provinces}
return render_template('index.html',**contents)
@app.route('/dj')
def dj():
return render_template('dj.html')
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
POST方法
POST方法和GET方法获取传入的值截然不同,POST方法用**request.form[' [参数名] ']**获取传入的参数值,和GET方法所介绍地一样,预定获取什么参数名就会获取传入地参数名中地参数
两种方法获取值地关键字不能互通,别把POST方法地form[]写成form()。
requests.form['name']
示例如下:
index.html
<html>
<head>
<title>测试</title>
</head>
<body>
<h1>Hello,{{user}}</h1>
<h2>年龄:{{age}}</h2>
<h3>省份:{{provinces}}</h3>
</body>
</html>
dj.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>信息登记</title>
</head>
<body>
<h1>个人信息登记</h1>
<div>
<form action="admin" method="post">
<p><input type="text" name="user" placeholder="请输入用户名"></p>
<p><input type='text' name='age' placeholder='请输入年龄'></p>
<p><input type='text' name='provinces' placeholder='请输入所在城市'></p>
<p><input type="submit" name="submit" value="提交"></p>
</form>
</div>
</body>
</html>
app.py
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/admin',methods=['GET','POST'])
def index():
user=request.form['user']
age=request.form['age']
provinces=request.form['provinces']
contents={'user':user,'age':age,'provinces':provinces}
return render_template('index.html',**contents)
@app.route('/dj')
def dj():
return render_template('dj.html')
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
redirect(重定向)
这个关键字在flask中用于重定向,需要配合url_for使用,url_for使用于构造url,比如常见的用法就是在登陆页面,输入正确的账号密码后,重定向到另外一个页面中
url_for() 函数用于构建指定函数的 URL,可以包含同文件下的其他路径,也可以包含外部文件。它把函数名称作为第一个 参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量。未知变量 将添加到 URL 中作为查询参数。
redirect和url_for也需要导入模块
from flask import Flask,redirect,url_for,request
return redirect('/')
return redirect(url_for('login_s'))
return redirect(url_for('login',name='admin'))
示例如下:
from flask import Flask,redirect,url_for,request
app = Flask(__name__)
@app.route('/')
def index():
name=request.args.get('name')
return 'Hello,{0}'.format(name)
@app.route('/user/<username>/<int:age>')
def user(username,age):
# return redirect('/')
return redirect(url_for('index',name=username))
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
模板渲染
一个复杂的html由多个文件组成,为了让模板更加美观,我们就需要多模板进行渲染,
单调的html看起来是枯燥乏味的,一个好看的html文件是有不同样式的文件组成的,因此,为了让模板看起来更好看,我们就需要对模板进行渲染,模板渲染需要注意一点,py文件和外部文件要放在同一个文件夹下,并且放置外部文件的文件夹名儿,要重命名为templates。
render_template
渲染外部文件,render_template默认去templates文件夹中寻找文件,可修改 app.template_folder 的值来修改模板存放文件夹名称,扩展名为.html、.htm、.xml和.xhtml 的模板中开启自动转义
在文件夹templates下创建index.html,内容如下:
<html>
<head>
<title>测试</title>
</head>
<body>
<h1>Hello,Word</h1>
</body>
</html>
一切准备就绪后,使用render_template将数据渲染。
app.py文件内容如下:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
如果有参数需要传递,则使用格式为"{{参数名}}"接受参数值,比如'user':'admin'
html文件中就使用<标签>{{user}}</标签>
单参数示例如下:
index.html
<html>
<head>
<title>测试</title>
</head>
<body>
<h1>Hello,{{user}}</h1>
</body>
</html>
app.py
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
user='admin'
return render_template('index.html',user=user)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
当参数为多个的时候,请使用形参的形式传出,有多个参数,可以使用**contents传过去,contents是自定义的,这样子参数值就会全部传入到index.html
多参数示例如下:
index.html
<html>
<head>
<title>测试</title>
</head>
<body>
<h1>Hello,{{user}}</h1>
<h2>年龄:{{age}}</h2>
<h3>省份:{{provinces}}</h3>
</body>
</html>
app.py
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
contents={'user':'admin','age':'19','provinces':'北京'}
return render_template('index.html',**contents)
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
render_template_string
render_template_string用于渲染字符串,这个可以用于没有外部文件的情况,直接在同文件下,定义好html代码,然后直接就可以渲染,render_template_string和render_template都是渲染,但是前者是字符串,后者是外部文件
render_template和render_template_string都需要导入才可以使用
from flask import Flask,render_template_string
app = Flask(__name__)
@app.route('/')
def index():
return render_template_string('<html><h1>测试</h1></html>')
if __name__ == '__main__':
app.debug = True
app.run('127.0.0.1','8080')
Jinja2模板全局变量
config 当前配置对象
request 当前请求对象
session 当前会话对象
g 请求绑定的全局变量
url_for
get_flashed_messages
模板的语法
分隔符
{{...}}:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。
{%...%}:装载一个控制语句。
{#...#}:装载一个注释,模板渲染的时候会忽视这个中间的值。
{{...}}
{{list.0}} 同 {{list[0]}}
{{dick.key}} 同 {{ dict.get(key) }}
{{obj.name}} 同 {{对象.属性}}
控制语句
Jinjia控制语句都是放在{% ... %}中,并且有一个语句{% endxxx %}来进行结束。
if语句
{% if age>18 %}
<div>你已满18岁</div>
{% elif age==18 %}
<div>你刚满18岁</div>
{% else %}
<div>你未满18岁</div>
{% endif %}
for循环
{% for list in lists %}
<div>姓名:{{list.name}}</div><div>年龄:{{list.age}}</div>
{% endfor %}
注意:不可以使用continue和break表达式来控制循环的执行。
Jinja2中for循环内置变量
loop.index | 当前迭代的索引(从1开始) |
---|---|
loop.index0 | 当前迭代的索引(从0开始) |
loop.first | 是否是第一次迭代,返回True / False |
loop.last | 是否是最后一次迭代,返回True / False |
loop.length | 序列的长度 |
loop.revindex | 索引是倒着排序(从1结束) |
loop.revindex0 | 索引是倒着排序(从0结束) |
示例:
{% for list in lists %}
<div>{{loop.index}}</div>
<div>姓名:{{list.name}}</div><div>年龄:{{list.age}}</div>
{% endfor %}
过滤器
过滤器相当于是一个函数,把当前变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。
过滤器是通过(|)符号进行使用的,例如:{{ name|length }}; 将返回name的长度。
语法
{{变量名|过滤器}}
{{变量名|过滤器(*args)}}
{{变量名|过滤器|过滤器}}
常见过滤器
过滤器名称 | 说明 |
---|---|
length | 返回长度 |
safe | 禁用转义,默认一些符号会被转义成html编码,禁用后则不转义 |
capitialize | 把值的首字母转换成大写,其他子母转换为小写 |
lower | 把值转换成小写形式 |
upper | 把值转换成大写形式 |
title | 把值中每个单词的首字母都转换成大写 |
reverse | 将值反转 |
format | 格式化,例:{{'%s is %d' | format('abc',11)}} |
truncate | 字符串截断,例:{{'hello word' | truncate(5)}} |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所有的HTML标签都删掉 |
join | 拼接多个值为字符串 |
replace | 替换字符串的值 |
round | 默认对数字进行四舍五入,也可以用参数进行控制 |
int | 把值转换成整型 |
sum | 求和,列表求和,要求参数为整型 |
sort | 排序,列表排序,要求参数为整型 |
其他过滤器可自行百度
自定义过滤器
方法
1、通过flask模块中的add_template_filter方法
2、使用装饰器完成
如果自定义的过滤器与原有过滤器重名,则会覆盖
示例
add_template_filter
def replace_script(value):
print('----->',value)
value=value.replace('script','')
print('----->',value)
return value
app.add_template_filter(replace_script,'replace')
装饰器
@app.template_filter('replace')
def replace_script(value):
print('----->',value)
value=value.replace('script','')
print('----->',value)
return value