内容:
1.基础的flask框架示例
2.flask配置文件
3.flask路由系统
4.请求和响应
5.模板
flask框架预备知识:https://www.cnblogs.com/wyb666/p/9460598.html
flask框架详细学习:http://www.cnblogs.com/wupeiqi/articles/7552008.html
1.基础的flask框架示例
(1)基础flask框架示例程序结构
其中app.py是主程序,utils.py是功能函数模块(log),templates文件夹中是HTML文件
(2)代码
程序功能:访问'/'显示欢迎信息和链接,访问'/message'显示留言板,然后可以用get提交数据,post提交数据,post提交的数据最后将显示在页面上
app.py:
1 from flask import ( 2 Flask, 3 render_template, 4 request, 5 redirect, 6 url_for, 7 ) 8 from utils import log 9 10 11 # 先要初始化一个 Flask 实例 12 app = Flask(__name__) 13 14 # message_list 用来存储所有的 message 15 message_list = [] 16 17 18 # 定义路由和路由处理函数的方式如下 19 # ========================== 20 # 用 app.route 函数定义路由,参数是一个 path 路径 21 # 下一行紧跟着的函数是处理这个请求的函数 22 # @ 是一个叫装饰器的东西, 现在无必要知道具体的原理, 只要用它就好了 23 # 注意 methods 参数是一个 list,它规定了这个函数能接受的 HTTP 方法 默认是GET 24 @app.route('/', methods=['GET']) 25 def hello_world(): 26 return '<h1>Hello wyb666</h1><br><a href="/message">message</a>' 27 28 29 @app.route('/message') 30 def message_view(): 31 log('请求方法', request.method) 32 # render_template 读取并返回 templates 文件夹中的模板文件 33 # messages 是传给模板的参数,这样就能在模板中使用这个变量 34 return render_template('message_index.html', messages=message_list) 35 36 37 # 这个路由函数只支持 POST 方法 38 @app.route('/message/add', methods=['POST']) 39 def message_add(): 40 log('message_add 请求方法', request.method) 41 42 # 把POST请求的数据生成一个 dict 存到 message_list 中去 43 msg = { 44 'content': request.form.get('msg_post', ''), 45 } 46 message_list.append(msg) 47 48 # 重定向: 49 return redirect('/message') 50 # 一般来说,我们会用 url_for 生成路由 注意url_for的参数是路由函数的名字(格式为字符串) 51 # return redirect(url_for('message_view')) 52 53 54 # 运行服务器 55 if __name__ == '__main__': 56 config = dict( 57 debug=True, # debug 模式可以自动加载你对代码的变动, 所以不用重启程序 58 host='localhost', # 指定域名 59 port=80, # 指定端口 60 ) 61 app.run(**config)
utils.py:
1 # 一些工具函数 2 import time 3 4 5 # 用 log 函数把所有输出写入到屏幕中 方便debug 6 def log(*args, **kwargs): 7 formats = '%Y/%m/%d %H:%M:%S' 8 value = time.localtime(int(time.time())) 9 dt = time.strftime(formats, value) 10 print(dt, *args, **kwargs)
message_index.html:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <!-- 指明字符编码为 utf-8 否则中文会乱码 --> 5 <meta charset="utf-8"> 6 <title>留言板</title> 7 </head> 8 <body> 9 <h1>留言板</h1> 10 <form action="/message" method="GET"> 11 这是一个发送 GET 请求的表单 12 <br> 13 <textarea name="msg"></textarea> 14 <br> 15 <button type="submit">用 GET 提交表单</button> 16 </form> 17 18 <form action="/message/add" method="POST"> 19 这是一个发送 POST 请求的表单 20 <br> 21 <textarea name="msg_post"></textarea> 22 <br> 23 <button type="submit">用 POST 提交表单</button> 24 </form> 25 <!-- 下面是使用模板的办法 --> 26 {# 这是 Jinja2 模板的注释语法,这样的注释并不会在生成的 HTML 代码中出现 #} 27 {# {% %} 里面的是语句 #} 28 {# {{ }} 里面的是引用变量 #} 29 <!-- 注意 m 本身是一个字典,但是你可以用 . 的语法来引用 --> 30 <!-- 这是 Jinja2 模板的功能 --> 31 {% for m in messages %} 32 <div>{{ m.content }}</div> 33 {% endfor %} 34 </body> 35 </html>
2.flask配置文件
(1)flask默认配置如下:
1 flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: 2 { 3 'DEBUG': get_debug_flag(default=False), # 是否开启Debug模式 4 'TESTING': False, # 是否开启测试模式 5 'PROPAGATE_EXCEPTIONS': None, 6 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 7 'SECRET_KEY': None, 8 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 9 'USE_X_SENDFILE': False, 10 'LOGGER_NAME': None, 11 'LOGGER_HANDLER_POLICY': 'always', 12 'SERVER_NAME': None, 13 'APPLICATION_ROOT': None, 14 'SESSION_COOKIE_NAME': 'session', 15 'SESSION_COOKIE_DOMAIN': None, 16 'SESSION_COOKIE_PATH': None, 17 'SESSION_COOKIE_HTTPONLY': True, 18 'SESSION_COOKIE_SECURE': False, 19 'SESSION_REFRESH_EACH_REQUEST': True, 20 'MAX_CONTENT_LENGTH': None, 21 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 22 'TRAP_BAD_REQUEST_ERRORS': False, 23 'TRAP_HTTP_EXCEPTIONS': False, 24 'EXPLAIN_TEMPLATE_LOADING': False, 25 'PREFERRED_URL_SCHEME': 'http', 26 'JSON_AS_ASCII': True, 27 'JSON_SORT_KEYS': True, 28 'JSONIFY_PRETTYPRINT_REGULAR': True, 29 'JSONIFY_MIMETYPE': 'application/json', 30 'TEMPLATES_AUTO_RELOAD': None, 31 }
(2)flask常用配置方法
1 方式一: 2 app.config['DEBUG'] = True 3 PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) 4 5 6 方式二: 7 app.config.from_pyfile("python文件名称") 8 eg: 9 settings.py: 10 DEBUG = True 11 12 app.py: 13 app.config.from_pyfile("settings.py") 14 15 16 方式三: 17 app.config.from_object("python类或类的路径") 18 eg: 19 settings.py: 20 class Config(object): 21 DEBUG = False 22 TESTING = False 23 DATABASE_URI = 'sqlite://:memory:' 24 25 class ProductionConfig(Config): 26 DATABASE_URI = 'mysql://user@localhost/foo' 27 28 class DevelopmentConfig(Config): 29 DEBUG = True 30 31 class TestingConfig(Config): 32 TESTING = True 33 34 app.py: 35 app.config.from_object('pro_flask.settings.TestingConfig') 36 37 PS: 从sys.path中已经存在路径开始写 38 39 40 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
3.flask路由系统
(1)常用路由
- @app.route('/user/<username>')
- @app.route('/post/<int:post_id>')
- @app.route('/post/<float:post_id>')
- @app.route('/post/<path:path>')
- @app.route('/login', methods=['GET', 'POST'])
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
1 DEFAULT_CONVERTERS = { 2 'default': UnicodeConverter, 3 'string': UnicodeConverter, 4 'any': AnyConverter, 5 'path': PathConverter, 6 'int': IntegerConverter, 7 'float': FloatConverter, 8 'uuid': UUIDConverter, 9 }
(2)flask路由系统本质(注册路由原理)
flask的路由系统比较特殊,是基于装饰器来实现,关于装饰器:http://www.cnblogs.com/wyb666/p/8748102.html,但是flask路由系统的本质是通过add_url_rule方法来实现,详情看下面:
一个基本的路由:
1 @app.route('/', methods=['GET'], endpoint="hello") 2 def hello_world(): 3 return '<h1>Hello wyb666</h1><br><a href="/message">message</a>'
第一行以@开头明显是一个装饰器,源码及其解释如下:
1 def route(self, rule, **options): 2 # 这是一个被用来给view函数注册路由的装饰器 3 # rule是URL规则 4 # options是一系列参数 比如methods(请求方法)、endpoint(反向查询URL)等 (endpoint不写默认就是函数名) 5 def decorator(f): 6 endpoint = options.pop('endpoint', None) 7 # add_url_rule其实就是将路由规则和相应的处理函数对应起来 8 # 类似django中的urls.py中的 9 self.add_url_rule(rule, endpoint, f, **options) 10 return f 11 return decorator
上面那个基本路由的本质:
1 @app.route('/', methods=['GET'], endpoint="hello") 2 def hello_world(): 3 pass 4 上面这段代码相当于: 5 1. decorator = app.route('/', methods=['GET'], endpoint="hello") 6 2. @decorator -> decorator(hello_world) 7 3. endpoint = options.pop('endpoint', None) 8 # 添加路由对应关系 9 self.add_url_rule(rule='/', endpoint="hello", f=hello_world, **options) 10 return f
于是这样写也可以:
1 def hello_world(): 2 return '你好!' 3 4 app.add_url_rule('/', "hello", hello_world, methods=['GET'])
总结:
flask的路由系统比较特殊,是基于装饰器来实现,但flask路由系统的本质是通过add_url_rule方法来实现,也就是说在本质上和django并无太大的区别,也可以像django那样使用路由,将上述代码进一步完善可以实现django中的FBV
当然也可以实现django中的CBV:
1 from flask import views 2 # 装饰器 3 def auth(func): 4 def inner(*args, **kwargs): 5 print('before') 6 result = func(*args, **kwargs) 7 print('after') 8 return result 9 return inner 10 11 class IndexView(views.MethodView): 12 methods = ['GET', 'POST'] 13 decorators = [auth, ] 14 15 def get(self): 16 return 'Index.GET' 17 18 def post(self): 19 return 'Index.POST' 20 21 # name其实就是endpoint 22 # as_view: 返回一个函数,将普通的view函数 -> 捆绑了路由的view函数 23 app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))
@app.route和app.add_url_rule的参数如下:
1 @app.route和app.add_url_rule参数: 2 rule, URL规则 3 view_func, 视图函数名称 4 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 5 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') 6 methods=None, 允许的请求方式,如:["GET","POST"] 7 8 9 strict_slashes=None, 对URL最后的 / 符号是否严格要求, 10 如: 11 @app.route('/index',strict_slashes=False), 12 访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可 13 @app.route('/index',strict_slashes=True) 14 仅访问 http://www.xx.com/index 15 16 redirect_to=None, 重定向到指定地址 17 如: 18 @app.route('/index/<int:nid>', redirect_to='/home/<nid>') 19 或 20 def func(adapter, nid): 21 return "/home/888" 22 @app.route('/index/<int:nid>', redirect_to=func) 23 24 subdomain=None, 子域名访问 25 from flask import Flask, views, url_for 26 27 app = Flask(import_name=__name__) 28 app.config['SERVER_NAME'] = 'wupeiqi.com:5000' 29 30 31 @app.route("/", subdomain="admin") 32 def static_index(): 33 """Flask supports static subdomains 34 This is available at static.your-domain.tld""" 35 return "static.your-domain.tld" 36 37 38 @app.route("/dynamic", subdomain="<username>") 39 def username_index(username): 40 """Dynamic subdomains are also supported 41 Try going to user1.your-domain.tld/dynamic""" 42 return username + ".your-domain.tld" 43 44 45 if __name__ == '__main__': 46 app.run()
(3)自定义正则路由匹配
很少用到,用到再看看下面的吧:
1 from flask import Flask, views, url_for 2 from werkzeug.routing import BaseConverter 3 4 app = Flask(import_name=__name__) 5 6 # 这个类一定要写,并且继承BaseConverter 7 class RegexConverter(BaseConverter): 8 """ 9 自定义URL匹配正则表达式 10 """ 11 def __init__(self, map, regex): 12 super(RegexConverter, self).__init__(map) 13 self.regex = regex 14 15 def to_python(self, value): 16 """ 17 路由匹配时,匹配成功后传递给视图函数中参数的值 18 :param value: 19 :return: 20 """ 21 return int(value) 22 23 def to_url(self, value): 24 """ 25 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 26 :param value: 27 :return: 28 """ 29 val = super(RegexConverter, self).to_url(value) 30 return val 31 32 # 添加到flask中 33 app.url_map.converters['regex'] = RegexConverter 34 35 36 @app.route('/index/<regex("d+"):nid>') 37 def index(nid): 38 print(url_for('index', nid='888')) 39 return 'Index' 40 41 42 if __name__ == '__main__': 43 app.run()
(4)蓝图与子域名实现
蓝图用于为应用提供目录划分和模块化路由的功能,在flask中,蓝图可以拥有自己的静态资源路径、模板路径,从而实现结构划分
子域名类似xxx.douban.com的形式,比如book.douban.com、movie.douban.com、music.douban.com、time.douban.com
关于这两者详情看此:https://www.cnblogs.com/wyb666/p/9568197.html
4.请求和响应
(1)flask请求
flask请求相关信息:
1 request.method -> 请求方法 2 request.args -> get请求参数 3 request.form -> post请求参数 4 request.values -> 所有请求参数 5 request.cookies -> 请求的cookies 6 request.headers -> 请求的headers 7 request.path -> 请求的路径 8 request.full_path -> 请求的完整路径 9 request.script_root 10 request.url 11 request.base_url 12 request.url_root 13 request.host_url 14 request.host 15 request.files -> 文件
关于flask上传文件:
1 前端: 2 <form action="" enctype='multipart/form-data' method='POST'> 3 <input type="file" name="file"> 4 <input type="submit" value="上传文件"> 5 </form> 6 flask: 7 file = request.files.get("file") # 获取文件 8 filename = file.filename # 获取文件名 9 file.save(os.path.join(FILE_DIR,filename)) # 保存文件
(2)flask响应
flask响应相关信息:
1 return "字符串" # 直接返回字符串 2 return render_template('html模板路径') # 返回HTML 3 return render_template('html模板路径', xxx=xxx) # 返回HTML并传参(xxx=xxx也可以写成**{}的形式) 4 return redirect('/index.html') # 重定向 5 6 response = make_response(render_template('index.html')) # response是flask.wrappers.Response类型 7 response.delete_cookie('key') # 删除cookie 8 response.set_cookie('key', 'value') # 设置cookie 9 response.headers['X-Something'] = 'A value' 10 return response
5.模板
(1)模板的使用
Flask使用的是Jinja2模板,其语法和Django无差别
(2)自定义模板方法
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template,然后将执行这个函数并将其最后结果替换到HTML中函数的位置,如:
1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <h1>自定义函数</h1> 9 {{func()|safe}} 10 11 </body> 12 </html>
1 from flask import Flask,render_template 2 3 app = Flask(__name__) 4 5 def index(): 6 return '<h1>index</h1>' 7 8 @app.route('/test', methods=['GET', 'POST']) 9 def test(): 10 # index.html就是上面的HTML 11 return render_template('index.html', func=index) 12 13 app.run()
注意为了防止XSS攻击,直接传HTML时会对HTML进行转义,HTML将以字符串的形式直接显示在页面上,需要在模板上函数调用后加上| safe才可以把HTML显示出来
flask模板定制功能:
1 @app.template_global() 2 def sb(a1, a2): 3 return a1 + a2 4 # HTML中调用方法: 5 # {{ sb(1, 2)}} # 1和2分别对应两个参数 6 7 @app.template_filter() 8 def db(a1, a2, a3): 9 return a1 + a2 + a3 10 # HTML中调用方法: 11 # {{ 1|db(2, 3)}} # 1对应第一个参数 2和3对应后面的参数