新篇章---flask---2
一 flask中的路由系统
1.1 @app.route()装饰器中的参数
1.1.0 首先,回顾装饰器写法:
def warpper(f):
def inner(*args,**kwargs):
print(123)
ret = f(*args,**kwargs)
return ret
return inner
@warpper
def f():
print(666)
return
f()
1.1.1 methods:
methods:当前url地址,允许访问的请求方式
from flask import Flask,redirect,request
app = Flask(__name__)
@app.route("/index", methods=["GET", "POST"])
def student_info():
stu_id = int(request.args["id"])
return f"Hello boy {stu_id}" # Python3.6的新特性 f"{变量名}"
if __name__ == '__main__':
app.run('0.0.0.0',5000,debug=True)
此时,输入url是http://192.168.16.18:5000/index?id=2,此时得到页面显示Hello boy 2.
1.1.2 endpoint(别名,参考django的反向解析)
endpoint:
概念:就是id(Identifier),类似django中url的别名(name=...),拿到反向url地址,默认是视图函数名,也可以自己修改
好处:使用endpoint后,我们可以随意改变应用中的URL地址,却不用修改与之关联的资源的代码。
过程:viewfunction–>endpoint–>URL
采用该过程的原因(不跳过endpoint):
原因就是采用这种方法能够使程序更高、更快、更强。例如蓝图。蓝图允许我们把应用分割为一个个小的部分,对于不同的应用, 蓝本允许把应用分割为一个个以命名空间区分的小部分(参考第二个实例)
理解一个小代码:
@app.route('/user/<name>')
def user(name):
return 'Hello, %s' % name
该函数等价于
def user(name)
return 'Hello, %s' % name
app.add_url_rule('/user/<name>', 'user', user)
add_url_rule有四个参数,不考虑options,前三个参数分别是rule,view_func,endpoint.
rule:匹配的路由地址.
view_func:我们正常写的视图函数.
endpoint:上面的那些了.
url_for是什么?---简单来说,url_for()是获取当前的url地址.里面需要的参数是端点(endpoint值),
补充:(里面的参数与解释(机器翻译))
:endpoint:URL的端点(函数名)。
:values:URL规则的变量参数。
: _external:如果设置为True,则生成绝对URL。伺服器。
可以通过SERVER_NAME配置变量更改地址,
回退到Host头部,然后返回到请求的IP和端口。
:_scheme:指定所需URL方案的字符串。_外部。参数必须设置为True或引发:exc:ValueError。缺省值。行为使用与当前请求相同的方案,或首选URL_SCHEME来自:ref:app configuration<config>if no。请求上下文可用。从Werkzeug 0.10开始,也可以设置。设置为空字符串以构建协议相关URL。
:_anchor:如果提供,则将其作为锚点添加到URL中。
:_method:如果提供,则显式指定HTTP方法。
端点(endpoint)就是程序中一组逻辑处理单元的ID,该ID对应的代码决定了对此ID请求应该作出何种响应。通常,端点与视图函数同名,但是你也可以修改它.
URL (http://www.example.org/greeting/Mark) 映射到端点"say_hello"上. 指向端点"say_hello"的请求被视图函数"give_greeting"处理.
端点通常用作反向查询URL地址(viewfunction–>endpoint–>URL)。例如,在flask中有个视图,你想把它关联到另一个视图上(或从站点的一处连接到另一处)。不用去千辛万苦的写它对应的URL地址,直接使用URL_for()就可以啦
参考链接1
参考链接2
参考链接3---
1.1.2.1 应用实例1(参考代码模板)
from flask import Flask, request, url_for
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/index', methods=["POST", "GET"], endpoint="r_info")
def student_info():
print(url_for('r_info'))
stu_id = int(request.args["id"])
return f"hello boy {stu_id}"
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
1.1.2.2 应用实例2(读书人的事,能说偷吗)
main.py
from flask import Flask, Blueprint
from admin import admin
from user import user
app = Flask(__name__)
app.register_blueprint(admin, url_prefix='admin')
app.register_blueprint(user, url_prefix='user')
admin.py
admin = Blueprint('admin', __name__)
@admin.route('/greeting')
def greeting():
return 'Hello, administrative user!'
user.py
user = Blueprint('user', __name__)
@user.route('/greeting')
def greeting():
return 'Hello, lowly normal user!'
1.1.2.3 对url_for的补理解和补充
概念:
url_for()函数用于构建指定函数的url.
url_for操作对象是函数(准确来说是通过endpoint操作view_func),而不是route里面的路径,返回的是路径(可操作自己想返回的路径(绝对/相对/自己拼接))
url_for()可以直接用view_func名进行查询,也可以用endpoint进行查询(实际上view_func名就是默认的endpoint(如果不进行修改的话))
使用url_for()的原因:
将来如果修改了'URL',但是没有修改URL对应的函数名,就不用到处去替换URLl
'url_for'会自动的处理那些特殊的字符,不需要去手动处理
url_for()的使用范例:
from flask import Flask, url_for
app = Flask(__name__)
# We can use url_for('foo_view') for reverse-lookups in templates or view functions
@app.route('/foo')
def foo_view():
pass
# We now specify the custom endpoint named 'bufar'. url_for('bar_view') will fail!
@app.route('/bar', endpoint='bufar')
def bar_view():
pass
with app.test_request_context('/'):
print (url_for('foo_view')) #/foo
print (url_for('bufar')) #/bar
# url_for('bar_view') will raise werkzeug.routing.BuildError
print (url_for('bar_view')) #端点bar_view是没有定义的
1.1.2.4 最终的代码(自己写的)
from flask import Flask, request, url_for
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/index', methods=["POST", "GET"], endpoint="r_info")
def student_info():
print(url_for('r_info',name='Mark',id=2))
#写name=Mark就会接到后面,`/index?name=Mark`,我们可以通过这种方式传递参数 ,
# 加上id=2就变成了`/index?name=Mark&id=2`
stu_id = int(request.args["id"])
return f"hello boy {stu_id}"
@app.route('/index/<page>')
def my_list(page):
return f'{page}'
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
url为`http://192.168.16.18:5000/index?id=3`(如果不写上id=3,怎么可能能接到呢?你写在url_for中只是为了传给前端,而前端写?id=3是为了从前端传输给后端,两个根本不同!!!)
1.1.3 defaults
defaults:视图函数的参数默认值(能给视图函数写入形参的方式之一)(另一种是用类似
from flask import Flask, request, url_for
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/index/<page>',methods=['POST','GET'],defaults={'page':123})
def my_list(page):
return f'{page}'
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
输入url为http://192.168.16.18:5000/index/12,此时显示123,并不显示12,说明使用的是默认的数值.(不能不输入12,不输入的话是404)
注意,default进行传值的时候要用字典dict.
1.1.4 strict_slashes
strict_slashes : url地址结尾符"/"的控制
False : 无论结尾 "/" 是否存在均可以访问 ,
True : 结尾必须不能是 "/"
使用地方:
from flask import Flask, request, url_for
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/index/<page>',methods=['POST','GET'],strict_slashes=True)
def my_list(page):
return f'{page}'
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
测试发现,True的时候加上/就是not found...
1.1.5 redirect_to:url地址重定向
没什么好说的,308永久重定向
from flask import Flask, request, url_for
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/index/<page>',methods=['POST','GET'],redirect_to='/index')
def my_list(page):
return f'{page}'
@app.route('/index')
def youxiu():
return 'sgoyi'
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
输入http://192.168.16.18:5000/index/12,跳转到http://192.168.16.18:5000/index.
1.2 动态参数路由
比如返回一个文件内容(如果返回其他目录的内容的话记得用os.path.join进行拼接)
from flask import Flask, request, url_for,send_file
app = Flask(__name__)
app.config['DEBUG'] = True
#如何返回一个文件内容
@app.route('/index/<filename>',methods=['POST','GET'])
def my_list(filename):
print(filename)
return send_file(filename)
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
结果返回的是这个文件内容.
1.3 补充(Markup)
如果防止HTML显示为字符串,flask采用Markup实现,而django采用make_safe。
#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/index')
def index():
return render_template('index.html')
@app.template_global() #simple_tag
def foo(arg):
return "<input type='text' value='{}'>".format(arg)
if __name__ == '__main__':
app.run()
此外,Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter功能。simple_tag(只能传2个参数,支持for、if)
二 实例化flask的参数和对app的配置
2.1
- Config对象配置(重点:快速切换debug,testing,线上环境配置)
static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录
static_host = None, # 远程静态文件所用的Host地址,默认为空
static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用
host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True
这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写
host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由
host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数
subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里
template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
instance_path = None, # 指向另一个Flask实例的路径
instance_relative_config = False # 是否加载另一个实例的配置
root_path = None # 主模块所在的目录的绝对路径,默认项目目录
然鹅,要记得并没有这么多.下面是常用的:
- static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录
- static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用
- template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
三 蓝图
3.1 概念
蓝图(blueprint):作用是将功能和主服务分开.
蓝图除了run,跟主程序差不多(有点像程序).内部的视图函数和route不要重复...(注意问题哦)
蓝图的目录结构:

3.2 步骤
- 第一步,创建一个项目并且把目录结构做成自己想要的结构...(考虑蓝图,settings配置)
- 在s.py文件里写蓝图.
- 导入到主程序中.
明显的我们没有在Flask对象中添加路由,但是我们注册了有路由和视图函数的sv蓝图对象
(尝试下蓝图的增删改查吧!!!)
蓝图的增删改查
3.3 蓝图代码
manage.py
from flask import Flask
#导入写好的蓝图模块
from blueprint.tester import bp1,bp2
app = Flask(__name__)
app.config['DEBUG'] = True
app.register_blueprint(bp1)
app.register_blueprint(bp2)
@app.route('/')
def te():
return {1:'234天下'}
if __name__ == '__main__':
app.run('0.0.0.0',5000,debug=True)
在blueprint的tester.py中有
from flask import Blueprint
bp1 = Blueprint('bp1',__name__)
@bp1.route('/bp1')
def test1():
return '时间过的好快啊'
bp2 = Blueprint('bp2',__name__)
@bp2.route('/bp2')
def test2():
return {'人为何能咸鱼':'因为有自知之明'}
其中,bp1和bp2就是蓝图.(可以把它看成一个py文件就是一类,里面写的东西都是一个性质的)
四 特殊的装饰器
4.1 before_request(在请求(request)进入视图函数之前执行)
from flask import Flask, request, render_template, redirect, session
# 导入写好的蓝图模块
from blueprint.viewer import login, index
app = Flask(__name__)
app.config['DEBUG'] = True
app.secret_key = 'asdfghjkjhrge'
# app.register_blueprint(login)
# app.register_blueprint(index)
# 特殊装饰器
@app.before_request
def is_login():
if request.path == '/login':
return None
if not session.get('uname'):
return redirect('login')
@app.route('/login', methods=["POST", 'GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
uname = request.form.get('username')
pwd = request.form['password']
if uname == '123' and pwd == '123':
session['uname'] = uname
return redirect('index')
else:
return redirect('login')
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run('0.0.0.0', 5000, debug=True)
还有after_request.
4.2 after_request(在响应(response)返回客户端之前执行 , 结束视图函数之后)
@app.after_request
def foot_log(environ):
if request.path != "/login":
print("有客人访问了",request.path)
return environ
这个很少有人用的哈...但要了解哦.
4.3 404页面(同属特殊的装饰器)
from flask import Flask, request, render_template, redirect, session
# 导入写好的蓝图模块
from blueprint.viewer import login, index
app = Flask(__name__)
# app.config['DEBUG'] = True
# app.secret_key = 'asdfghjkjhrge'
# app.register_blueprint(login)
# app.register_blueprint(index)
# 类导入配置(注意使用app.default_config(如果忘记了的话))
from config_mode.settings import DebugSettings, TestSettings
app.config.from_object(DebugSettings)
# app.config.from_object(TestSettings)
# 特殊装饰器
@app.before_request
def is_login():
if request.path == '/login':
return None
if not session.get('uname'):
return redirect('login')
# 报错信息(404的)
@app.errorhandler(404)
def error404(error_message):
print(error_message)
return redirect(
'https://gitee.com/python_stack_20/teaching_plan/issues/IZ16D%E6%92%92%E6%89%93%E9%A3%9E%E6%9C%BA%E8%80%83%E6%A0%B8%E6%B3%95%E7%AC%AC%E4%B8%89%E6%96%B9%E7%9A%84%E8%90%A8%E5%87%A1%E7%BA%B3%E7%9A%84v')
@app.route('/login', methods=["POST", 'GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
uname = request.form.get('username')
pwd = request.form['password']
if uname == '123' and pwd == '123':
session['uname'] = uname
return redirect('index')
else:
return redirect('login')
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
五 问题
if blueprint.name in self.blueprints:
AttributeError: 'function' object has no attribute 'name'
如果出现这个问题的时候,一般情况是蓝图的名字和主程序的名字一致造成的.
正确实例:(注意,实例化的蓝图名字不能和函数名相同,这就是上面问题的根源)
from flask import Blueprint, request, redirect, session, render_template
login1 = Blueprint('login1', __name__)
@login1.route('/login', methods=["POST", 'GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
uname = request.form.get('username')
pwd = request.form['password']
if uname == '123' and pwd == '123':
session['uname'] = uname
return redirect('index1')
else:
return redirect('login1')
index1 = Blueprint('index1', __name__)
@index1.route('/index')
def index():
return render_template('index.html')
之前写错的代码:
from flask import Blueprint, request, redirect, session, render_template
login1 = Blueprint('login1', __name__)
@login1.route('/login', methods=["POST", 'GET'])
def login1():
if request.method == 'GET':
return render_template('login.html')
else:
uname = request.form.get('username')
pwd = request.form['password']
if uname == '123' and pwd == '123':
session['uname'] = uname
return redirect('index1')
else:
return redirect('login1')
index1 = Blueprint('index1', __name__)
@index1.route('/index')
def index1():
return render_template('index.html')
六 最终代码
views.py
from flask import Blueprint, request, redirect, session, render_template
login1 = Blueprint('login', __name__)
@login1.route('/login', methods=["POST", 'GET'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
uname = request.form.get('username')
pwd = request.form['password']
if uname == '123' and pwd == '123':
session['uname'] = uname
return redirect('index')
else:
return redirect('login')
index1 = Blueprint('index', __name__)
@index1.route('/index')
def index():
return render_template('index.html')
settings.py
class DebugSettings(object):
DEBUG = True
SECRET_KEY = 'adsfhkjhgf'
SESSION_COOKIE_NAME = 'I am debug session'
class TestSettings(object):
TESTING = True
SECRET_KEY = 'asfsafghgjhkhgsfad'
SESSION_COOKIE_NAME = 'I am Not session'
s1.py
from flask import Flask, request, render_template, redirect, session
# 导入写好的蓝图模块
from blueprint.viewer import login1, index1
app = Flask(__name__)
# app.config['DEBUG'] = True
# app.secret_key = 'asdfghjkjhrge'
# 类导入配置(注意使用app.default_config(如果忘记了的话))
from config_mode.settings import DebugSettings, TestSettings
app.config.from_object(DebugSettings)
# app.config.from_object(TestSettings)
# 特殊装饰器
@app.before_request
def is_login():
if request.path == '/login':
return None
if not session.get('uname'):
return redirect('login')
# 报错信息(404的)
@app.errorhandler(404)
def error404(error_message):
print(error_message)
return redirect('https://gitee.com/python_stack_20/teaching_plan/issues/IZ16D%E6%92%92%E6%89%93%E9%A3%9E%E6%9C%BA%E8%80%83%E6%A0%B8%E6%B3%95%E7%AC%AC%E4%B8%89%E6%96%B9%E7%9A%84%E8%90%A8%E5%87%A1%E7%BA%B3%E7%9A%84v')
# @app.route('/login', methods=["POST", 'GET'])
# def login():
# if request.method == 'GET':
# return render_template('login.html')
# else:
# uname = request.form.get('username')
# pwd = request.form['password']
# if uname == '123' and pwd == '123':
# session['uname'] = uname
# return redirect('index')
# else:
# return redirect('login')
#
#
# @app.route('/index')
# def index():
# return render_template('index.html')
app.register_blueprint(login1)
app.register_blueprint(index1)
if __name__ == '__main__':
app.run('0.0.0.0', 5000)
七 之前的问题的补充
7.1 对装饰器的补充
解决一个装饰器用于多个函数的装饰的方法.
方法一:加入endpoint=函数名(避免重复,都是inner)

方法二:(导入warps模块,使用的时候避免都用inner作为函数名)

7.2 关于flask直接返回字典的问题
可以直接返回字典,但仅限于ASCII码的数据,也就是说,你输入中文不识别的!!!
浙公网安备 33010602011771号