【Python】【Flask】
【快速开始】
from flask import Flask, url_for, render_template, redirect
app = Flask(__name__)
"""
# kaishi
@app.route('/')
def index():
return 'Index Page'
@app.route('/hello')
def hello():
return 'Hello Flask World!!'
# param
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % (username)
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % (post_id)
# 重定向
@app.route('/projects/')
def projects():
return 'The projects page'
@app.route('/about')
def about():
return 'The about page'
# eg.. url_for 1
@app.route('/')
def index():
return 'hello'
@app.route('/login')
def login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return 'profile %s' % username
with app.test_request_context():
print (url_for('index'))
print (url_for('login'))
print (url_for('login', ui='/'))
print (url_for('profile', username='Jone'))
'''
/
/login
/login?ui=%2F
/user/Jone
'''
# eg.. url_for 2
@app.route('/')
def index():
return 'hello'
@app.route('/login')
def Login():
return 'login'
@app.route('/user/<username>')
def profile(username):
return 'profile %s' % username
with app.test_request_context():
print (url_for('index'))
print (url_for('login'))
print (url_for('login', ui='/'))
print (url_for('profile', username='Jone'))
'''
werkzeug.routing.BuildError: Could not build url for endpoint 'login'. Did you mean 'Login' instead?
'''
#eg..
@app.route('/user/<username>/<userid>')
def user_index(username, userid):
return render_template('index.html', username=username)
@app.route('/')
def index():
return redirect(url_for('user_index', username='default', userid=99))
'''
网站输入http://localhost:5000/
自动指向http://localhost:5000/user/default/99
网页内容是test_one.html内容
<index.html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
LOGIN IN!!
</body>
</html>
【注意】模版的位置放在templates文件夹下,一般是html文件
'''
@app.route('/')
def index():
list1 = list(range(10))
my_list = [{'id': 1, 'value': '我爱工作'},
{'id': 2, 'value': '工作使人快乐'},
{'id': 3, 'value': '沉迷于工作无法自拔'},
{'id': 4, 'value': '日渐消瘦'},
{'id': 5, 'value': '以梦为马,越骑越瘦'}]
return render_template(
#渲染模版语言
'index.html',
title = 'hello flask world',
list2 = list1,
my_list = my_list
)
#step1 定义过滤器
def do_listreverse(li):
temp_li = list(li)
temp_li.reverse()
return temp_li
#step2 添加自定义过滤器
app.add_template_filter(do_listreverse, 'listreverse')
'''
网站输入http://localhost:5000/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{title|reverse|upper}}</h1>
<br>
{{list2 | listreverse}}
<br>
<ul>
{% for item in my_list %}
<li>{{item.id}}---{{item.value}}</li>
{% endfor %}
</ul>
{% for item in my_list %}
{% if loop.index==1 %}
<li style="">{{loop.index}}--{{item.get('value')}}</li>
{% elif loop.index==2 %}
<li style="">{{loop.index}}--{{item.value}}</li>
{% elif loop.index==3 %}
<li style="">{{item.id}}--{{item.value}}</li>
{% else %}
<li style="">{{item.id}}--{{item.value}}</li>
{% endif %}
{% endfor %}
</body>
</html>
【备注】
常见内建过滤器
字符串操作
safe:禁用转义
<p>{{ '<em>hello</em>' | safe }}</p>
capitalize:把变量值的首字母转成大写,其余字母转小写
<p>{{ 'hello' | capitalize }}</p>
lower:把值转成小写
<p>{{ 'HELLO' | lower }}</p>
upper:把值转成大写
<p>{{ 'hello' | upper }}</p>
title:把值中的每个单词的首字母都转成大写
<p>{{ 'hello' | title }}</p>
reverse:字符串反转
<p>{{ 'olleh' | reverse }}</p>
format:格式化输出
<p>{{ '%s is %d' | format('name',17) }}</p>
striptags:渲染之前把值中所有的HTML标签都删掉
<p>{{ '<em>hello</em>' | striptags }}</p>
truncate: 字符串截断
<p>{{ 'hello every one' | truncate(9)}}</p>
列表操作
first:取第一个元素
<p>{{ [1,2,3,4,5,6] | first }}</p>
last:取最后一个元素
<p>{{ [1,2,3,4,5,6] | last }}</p>
length:获取列表长度
<p>{{ [1,2,3,4,5,6] | length }}</p>
sum:列表求和
<p>{{ [1,2,3,4,5,6] | sum }}</p>
sort:列表排序
<p>{{ [6,2,3,1,5,4] | sort }}</p>
语句块操作
{% filter upper %}
#一大堆文字#
{% endfilter %}
'''
# egg.. 静态文件
@app.route('/')
def template1():
democss = url_for('static', filename='css/demo.css')
print (democss)
return render_template('index.html', democss=democss)
'''
【/templates/index.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--
这是第一种方式
<link href="/static/css/demo.css" rel="stylesheet" type="text/css"/>
这是第二种方式
-->
<link href="{{democss}}" rel="stylesheet" type="text/css"/>
</head>
<body>
<h1>Hello Flask World!!</h1>
</body>
</html>
【/static/css/demo.css】
body{
color:red;
}
【执行】
浏览器访问http://localhost:5000
'''
# egg.. GET POST request
from flask import Flask,request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return '<h1>Home</h1>'
@app.route('/signin', methods=['GET'])
def signin_form():
return '''
<form action="/signin" method="post">
<p><input name="username"></p>
<p><input name="password" type="password"></p>
<p><button type="submit">Sign in</button></p>
</form>
'''
@app.route('/signin', methods=['POST'])
def signin():
#需要从request对象读取表单内容
if request.form['username']=='admin' and request.form['password']=='password':
return '<h3>Hello , admin!!</h3>'
return '<h3>Bad username or password .</h3>'
'''
wang xue, [14.05.19 11:44]
运行python app.py,Flask自带的Server在端口5000上监听:
$ python app.py
* Running on http://127.0.0.1:5000/
打开浏览器,输入首页地址http://localhost:5000/:
首页显示正确!
再在浏览器地址栏输入http://localhost:5000/signin,会显示登录表单:
输入预设的用户名admin和口令password,登录成功:
输入其他错误的用户名和口令,登录失败:
'''
# egg.. 廖雪峰模版的例子
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('home.html')
@app.route('/signin', methods=['GET'])
def signin_form():
return render_template('form.html')
@app.route('/signin', methods=['POST'])
def signin():
username = request.form['username']
password = request.form['password']
if username=='admin' and password=='password':
return render_template('signin-ok.html', username=username)
return render_template('form.html', message='Bad username or password', username=username)
'''
【/templates/home.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<h1 style="font-style: italic">Home</h1>
</body>
</html>
【/templates/form.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Please Sign In</title>
</head>
<body>
{% if message %}
<p style="color:red">{{ message }}</p>
{% endif %}
<form action="/signin" method="post">
<legend>Please sign in:</legend>
<p><input name="username" placeholder="Username" value="{{ username }}"></p>
<p><input name="password" placeholder="Password" type="password"></p>
<p><button type="submit">Sign In</button></p>
</form>
</body>
</html>
【/templates/sign-ok.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome, {{ username }}</title>
</head>
<body>
<p>Welcome, {{ username }}</p>
</body>
</html>
'''
# egg .. 上下文
'''
https://www.cnblogs.com/Erick-L/p/6991079.html
Flask Markup 上下文,request
在模板渲染中,使用Markup转换变量中的特殊字符
from flask import Markup
Markup函数对字符串进行转移处理再传递给render_template()函数
在浏览器中显示标签代码
路由地址的反响生成
通过函数名获得与其绑定的Url地址
需要使用url_for函数进行反向解析
with app.text_request_context()
print(url_for('f_root')) # 输出:/
app.text_request_context()方法告诉解释器为在其作用域中的代码模拟一个HTTP请求上下文,使其好像被一个HTTP请求所调用
使用Context上下文
他是服务器端获得应用及请求相关信息的对象
1、会话上下文
会话(session)是一种客户端与服务器端保持状态的解决方案,会话上下文是用来实现这种解决方案的存储结构
复制代码
from flask import Flask,session
from datetime import datetime
app = Flask(__name__)
app.secret_key = 'SET_ME_BEFORE_USE_SESSION'
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/write_session')
def wirteSession():
session['key_time']=datetime.now().strftime('%Y-%m-%d %H:%M:%S')# 将当前时间保存在Session中
return session['key_time'] # 返回当前时间
@app.route('/read_session')
def readSession():
return session.get('key_time')# 获取上次调用wirteSession时写入的时间并返回
复制代码
除了正常的数据保存和读取,flask.session对象还维护自身的状态,通过
new 判断本次请求的Session是否时新建的
modified 判断本次请求中是否修改过Session键值
@app.route('/write_session')
def wirteSession():
session['key_time']=time.time() # 将当前时间保存在Session中
return session.modified # 因为之前进行了Session设置,所以判断本次请求是否被修改过(modified)返回TRUE
应用全局对象
复制代码
from flask import Flask,g
class MYDB():
def __init__(self):
print('一个数据库链接已经建立')
def close(self):
print('数据库已经关闭')
def connect_to_database():
return MYDB()
def get_db():
db = getattr(g,'_database',None)
if db is None:
db = connect_to_database()
g._database = db # 存入Flask.g对象中
return db
@app.teardown_request # 在请求结束时自动被Flask框架调用
def teardown_db(response):
db = getattr(g,'_database',None)# 从Flask.g对象中获取对象,检查是否有链接数据库对象,如果有则关闭
if db is not None:
db.close()
复制代码
可以在请求处理函数的任何地方调用get_db()
复制代码
class MYDB():
def __init__(self):
print('一个数据库链接已经建立')
def close(self):
print('数据库已经关闭')
def connect_to_database():
return MYDB()
def get_db():
db = getattr(g,'_database',None)
if db is None:
db = connect_to_database()
g._database = db # 存入Flask.g对象中
return db
@app.teardown_request
def teardown_db(response):
db = getattr(g,'_database',None)# 从Flask.g对象中获取对象
if db is not None:
db.close()
def login():
db=get_db() # 第一次调用getdb 创建数据库链接
session['has_login']=True
# 使用db检查数据库中的用户名和密码
def view_list():
if 'has_login' not in session:
login()
db = get_db() # 第二次调用get_db()# 直接复用之前建立的链接
# 使用db 从数据库查询数据,返回teardown_db()将会被自动调用
复制代码
请求上下文生命周期
复制代码
from flask import Flask, g, request
app = Flask(__name__)
@app.before_request
def before_request():
print 'before request started'
print request.url
@app.before_request
def before_request2():
print 'before request started 2'
print request.url
g.name="SampleApp"
@app.after_request
def after_request(response):
print 'after request finished'
print request.url
response.headers['key'] = 'value'
return response
@app.teardown_request
def teardown_request(exception):
print 'teardown request'
print request.url
@app.route('/')
def index():
return 'Hello, %s!' % g.name
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
复制代码
访问”http://localhost:5000/”后,会在控制台输出:
复制代码
before request started
http://localhost:5000/
before request started 2
http://localhost:5000/
after request finished
http://localhost:5000/
teardown request
http://localhost:5000/
复制代码
request对象只有在请求上下文的生命周期内才可以访问。离开了请求的生命周期,其上下文环境也就不存在了,自然也无法获取request对象。而上面介绍的几个由上下文装饰器修饰的Hook函数,会挂载在请求生命周期内的不同阶段,所以其内部可以访问request对象。
构建请求上下文环境
我们使用Flask的内部方法”request_context()”来构建一个客户端请求上下文。
复制代码
from werkzeug.test import EnvironBuilder
ctx = app.request_context(EnvironBuilder('/','http://localhost/').get_environ())
ctx.push()
try:
print request.url
finally:
ctx.pop()
复制代码
“request_context()”会创建一个请求上下文”RequestContext”类型的对象,其需接收”werkzeug”中的”environ”对象为参数。”werkzeug”是Flask所依赖的WSGI函数库
我们可以在客户端的请求之外访问request对象,其实此时的request对象即是刚创建的请求上下文中的一个属性”request == ctx.request”。启动Flask时,控制台仍然可以打印出访问地址”http://localhost/”。上面的代码可以用with语句来简化:
from werkzeug.test import EnvironBuilder
with app.request_context(EnvironBuilder('/','http://localhost/').get_environ()):
print request.url
请求上下文的实现方式
对于Flask Web应用来说,每个请求就是一个独立的线程。请求之间的信息要完全隔离,避免冲突,这就需要使用本地线程环境(ThreadLocal),
”ctx.push()”方法,会将当前请求上下文,压入”flask._request_ctx_stack”的栈中,同时这个”_request_ctx_stack”栈是个ThreadLocal对象,也就是”flask._request_ctx_stack”看似全局对象,其实每个线程的都不一样。请求上下文压入栈后,再次访问其都会从这个栈的顶端通过”_request_ctx_stack.top”来获取,所以取到的永远是只属于本线程中的对象,这样不同请求之间的上下文就做到了完全隔离。请求结束后,线程退出,ThreadLocal线程本地变量也随即销毁,”ctx.pop()”用来将请求上下文从栈里弹出,避免内存无法回收。
——————————————————————————————————————————————————————————————————————
主要是在服务端获得从客户端提交的数据,包括url参数,表单数据,cookies等
复制代码
from flask import Flask,request,url_for,redirect
app = Flask(__name__)
@app.route('/redirect_url')
def redirect_url():
next = request.args.get('next') or url_for('index')
return redirect(next)
@app.route('/echo_url')
def echo_url():
return request.base_url
复制代码
自定义上下文变量和函数
自定义变量
from flask import current_app
@app.context_processor
def appinfo():
return dict(appname=current_app.name)
函数返回的是一个字典,里面有一个属性”appname”,值为当前应用的名称。我们曾经介绍过,这里的”current_app”对象是一个定义在应用上下文中的代理。函数用”@app.context_processor”装饰器修饰,它是一个上下文处理器,它的作用是在模板被渲染前运行其所修饰的函数,并将函数返回的字典导入到模板上下文环境中,与模板上下文合并。然后,在模板中”appname”成为了可访问的上下文对象。我们可以在模板中将其输出:
<p>Current App is: {{ appname }}</p>
自定义函数
同理我们可以自定义上下文函数,只需将上例中返回字典的属性指向一个函数即可,下面我们就来定义一个上下文函数来获取系统当前时间:
复制代码
import time
@app.context_processor
def get_current_time():
def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
return time.strftime(timeFormat)
return dict(current_time=get_time)
复制代码
<p>Current Time is: {{ current_time() }}</p>
<p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>
应用上下文环境
current_app代理
复制代码
from flask import Flask, current_app
app = Flask('SampleApp')
@app.route('/')
def index():
return 'Hello, %s!' % current_app.name
复制代码
我们可以通过”current_app.name”来获取当前应用的名称,也就是”SampleApp”。”current_app”是一个本地代理,它的类型是”werkzeug.local. LocalProxy”,它所代理的即是我们的app对象,也就是说”current_app == LocalProxy(app)”。使用”current_app”是因为它也是一个ThreadLocal变量,对它的改动不会影响到其他线程。你可以通过”current_app._get_current_object()”方法来获取app对象。
既然是ThreadLocal对象,那它就只在请求线程内存在,它的生命周期就是在应用上下文里。离开了应用上下文,”current_app”一样无法使用
构建应用上下文环境
同请求上下文一样,我们也可以手动构建应用上下文环境:
with app.app_context():
print current_app.name
“app_context()”方法会创建一个”AppContext”类型对象,即应用上下文对象,此后我们就可以在应用上下文中,访问”current_app”对象了。
应用上下文Hook函数
应用上下文也提供了装饰器来修饰Hook函数,不过只有一个”@app.teardown_appcontext”。它会在应用上下文生命周期结束前,也就是从”_app_ctx_stack”出栈时被调用。我们可以加入下面的代码,顺便也验证下,是否应用上下文在每个请求结束时会被销毁。
@app.teardown_appcontext
def teardown_db(exception):
print 'teardown application'
request的属性
下面是request可使用的属性,其中黑体是比较常用的。
form
一个从POST和PUT请求解析的 MultiDict(一键多值字典)。
args
MultiDict,要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性:
searchword = request.args.get('key', '')
values
CombinedMultiDict,内容是form和args。
可以使用values替代form和args。
cookies
顾名思义,请求的cookies,类型是dict。
stream
在可知的mimetype下,如果进来的表单数据无法解码,会没有任何改动的保存到这个·stream·以供使用。很多时候,当请求的数据转换为string时,使用data是最好的方式。这个stream只返回数据一次。
headers
请求头,字典类型。
data
包含了请求的数据,并转换为字符串,除非是一个Flask无法处理的mimetype。
files
MultiDict,带有通过POST或PUT请求上传的文件。
environ
WSGI隐含的环境配置。
method
请求方法,比如POST、GET。
path
script_root
url
base_url
url_root
如果用户请求如下URL:
http://www.example.com/myapplication/page.html?x=y
以上的参数内容如下:
名称 内容
path /page.html
script_root /myapplication
base_url http://www.example.com/myapplication/page.html
url http://www.example.com/myapplication/page.html?x=y
url_root http://www.example.com/myapplication/
is_xhr
如果请求是一个来自JavaScript XMLHttpRequest的触发,则返回True,这个只工作在支持X-Requested-With头的库并且设置了XMLHttpRequest。
blurprint
蓝本名字。
endpoint
endpoint匹配请求,这个与view_args相结合,可是用于重构相同或修改URL。当匹配的时候发生异常,会返回None。
get_json(force=False, silent=False, cache=True)
json
如果mimetype是application/json,这个参数将会解析JSON数据,如果不是则返回None。
可以使用这个替代get_json()方法。
max_content_length
只读,返回MAX_CONTENT_LENGTH的配置键。
module
如果请求是发送到一个实际的模块,则该参数返回当前模块的名称。这是弃用的功能,使用blueprints替代。
on_json_loading_failed(e)
routing_exception = None
如果匹配URL失败,这个异常将会/已经抛出作为请求处理的一部分。这通常用于NotFound异常或类似的情况。
url_rule = None
内部规则匹配请求的URL。这可用于在URL之前/之后检查方法是否允许(request.url_rule.methods) 等等。
默认情况下,在处理请求函数中写下
print('request.url_rule.methods', request.url_rule.methods)
会打印:
request.url_rule.methods {‘GET’, ‘OPTIONS’, ‘HEAD’}
view_args = None
一个匹配请求的view参数的字典,当匹配的时候发生异常,会返回None。
'''
# egg. Cookies
from flask import request, make_response
app = Flask(__name__)
@app.route('/')
def index():
resp = make_response(render_template('index.html'))
resp.set_cookie('username', 'wangxue')
return resp
@app.route('/en')
def en():
username = request.cookies.get('username')
return username
'''
【index.html】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Hello Flask !!
</body>
</html>
首先访问localhost:5000 : 页面显示"Hello Flask"
再次访问localhost:5000/en: 页面显示"wangxue"
'''
# egg.. 重定向和错误
from flask import Flask, abort, redirect, url_for, render_template
app = Flask(__name__)
@app.route('/')
def index():
return redirect(url_for('login'))
@app.route('/login')
def login():
abort(401)
this_is_never_executed()
def this_is_never_executed():
print ('*********************')
'''
访问 localhost:5000
页面显示:
Unauthorized
The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.
如果再加上下面代码,则页面显示制定html内容
@app.errorhandler(401)
def page_not_auth(error):
return render_template('page_not_auth.html'), 401
'''
# egg.. 响应
一个视图函数的返回值会被自动转换为一个响应对象。如果返回值是一个字符串,它被转换成一个响应主体是该字符串,错误代码为 200 OK ,媒体类型为 text/html 的响应对象。 Flask 把返回值转换成响应对象的逻辑如下:
如果返回的是一个合法的响应对象,它会被从视图直接返回。
如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
如果返回的是一个元组而且元组中元素能够提供额外的信息。这样的元组必须是 (response, status, headers) 形式且至少含有一个元素。 status 值将会覆盖状态代码,headers 可以是一个列表或额外的消息头值字典。
如果上述条件均不满足,Flask 会假设返回值是一个合法的 WSGI 应用程序,并转换为一个请求对象。
如果你想要获取在视图中得到的响应对象,你可以用函数 make_response() 。
想象你有这样一个视图:
@app.errorhandler(404)
def not_found(error):
return render_template('error.html'), 404
你只需要用 make_response() 封装返回表达式,获取结果对象并修改,然后返回它:
@app.errorhandler(404)
def not_found(error):
resp = make_response(render_template('error.html'), 404)
resp.headers['X-Something'] = 'A value'
return resp
# egg... 会话
除了请求对象,还有第二个称为 session 对象允许你在不同请求间存储特定用户的信息。 这是在 cookies 的基础上实现的,并且在 cookies 中使用加密的签名。这意味着用户可以查看 cookie 的内容, 但是不能修改它,除非它知道签名的密钥。
要使用会话,你需要设置一个密钥。这里介绍会话如何工作:
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
@app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
@app.route('/logout')
def logout():
# remove the username from the session if it's there
session.pop('username', None)
return redirect(url_for('index'))
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
这里提到的 escape() 可以在你不使用模板引擎的时候做转义(如同本例)。
怎样产生一个好的密钥
随机的问题在于很难判断什么是真随机。一个密钥应该足够随机。你的操作系统可以基于一个密码随机生成器来生成漂亮的随机值,这个值可以用来做密钥:
>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
把这个值复制粘贴到你的代码,你就搞定了密钥。
使用基于 cookie 的会话需注意: Flask 会将你放进会话对象的值序列化到 cookie。如果你试图寻找一个跨请求不能存留的值, cookies 确实是启用的,并且你不会获得明确的错误信息,检查你页面请求中 cookie 的大小,并与 web 浏览器所支持的大小对比。
"""
if __name__ == '__main__':
app.debug = True
app.run(host='0.0.0.0')