Flask: Quickstart解读
Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2
从示例代码说起:
1 from flask import Flask 2 app = Flask(__name__) 3 4 @app.route('/') 5 def hello_world(): 6 return 'Hello, World!'
第1行从flask模块导入Flask类;
第2行定义一个Flask实例,每一个Flask实例都是一个WSGI应用(Application);
参数是否为__name__是由模块是否 作为应用启动 还是 作为模块导入。在示例中,只有一个模块,并且是作为应用启动,因此,参数为__name__。
在Flask的介绍文档中,Flask(...)函数是有多个参数的,这些参数用于指定应用怎么寻找模板、静态文件等。
通常来讲,用户需要在 主模块或__init__.py文件 中创建Flask实例。
第4行使用route函数告知Flask在接收到URL位“/”时交由hello_world函数处理;
说明,截止此时, 我仍然不清楚如何创建更复杂的Flask应用——包含静态文件、模板文件、配置文件等的应用。
在将示例程序改造为包形式时出现了下面的问题:
1.文件夹下新建了__init__.py,并将Flask实例的创建代码放入其中;
1 from flask import Flask 2 3 app = Flask('HelloWorld')
2.改造后的hello.py
1 @app.route("/") 2 def hello(): 3 return "Hello World!"
3.运行应用
跳转到HelloWorld的上一级目录Flask,下图为执行情况:Flask应用启动成功
4.访问页面
发生错误,没有显示hello.py模块中输出的Hello World!,如下图:
后面又做了一下尝试,在__init__.py中添加了下面这句:
from HelloWorld import hello
结果,服务器运行不起来了,提示:
File "C:\Python36\ws\Flask\HelloWorld\hello.py", line 1, in <module>
@app.route("/")
NameError: name 'app' is not defined
app是在__init__.py中定义的,而hello.py模块并没有权限获取它吧?
然后给__init__.py的app前加一个global关键字,结果,提示语法错误!
app的定义要和其它URL的route调用放在同一个模块文件中?
……
接下来,怎么办?(一筹莫展也和自己的Python基础有关系吧)
----
在运行Flask项目时发现一个陷阱:Windows下SET命令设置环境变量时,等号前后不能有空格。
下图是有空格的,启动Flask项目失败:
下图么有空格,启动Flask项目成功:
----
启动Flask项目的两种方式
1.使用python -m flask run命令
2.使用flask run命令(Python的Scripts添加到Windows的Path环境变量后可用)
flask run还有更多参数可用,可以查看其帮助信息(flask run --help):
补充说明:
1.Flask项目如上面一样启动后,只能在本机访问;怎么让Flask项目可以被同网络的其它主机访问呢?使用-h/--host配置项绑定网卡地址;
疑问:不知道是否支持IPv6地址。
2.使用-h/--host配置项时,可以将参数设置为0.0.0.0,表示从本机的任何网卡都可以访问,如果固定到某一个IP地址,则只是绑定了那个IP地址所在
网卡,此时只有那个IP所在网络的主机可以访问;
在设置了-h/--host配置项启动时,Windows弹出了报警窗口:
3.Flask项目的默认端口是5000,使用-p/--port来指定;
还要检查所配置的端口是否被防火墙等软件屏蔽了,若是,需要开启。
还有其它配置项,还需更多了解。
----
开启调试模式,只需要在Flask项目启动时设置环境变量FLASK_ENV为development即可。
当然,前面所述,也可以在启动时使用--debugger配置项打开。
注意,一定要在生产环境中把调试器关闭!否则会有很大的安全隐患!
使用调试器的更多信息,还需要学习实践。
--
Routing
可以翻译为 URL路由,即定义Flask如何将各个URL请求进行转发,转发到函数,或者其它地方(哪里?尚不清楚)。
route()函数用于将某个函数绑定到URL。
示例:
@app.route('/')
@app.route('/hello')
上面两个都是静态的URL,开发人员可以将URL变为动态的并绑定多个规则到一个函数(N URLs TO a FUNCTION)。
Quickstart中介绍了下面几种:
1.变量规则(Variable Rules)
在URL中添加变量部分:<变量名>
函数会将收到的<变量名>作为关键词参数。
还可以选择用转换器指定参数的类型:<转换器:变量名>
转换器类型有5种:string, int, float, path, uuid (UUID string,见uuid模块)
示例:
@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/path/<path:subpath>')
2.唯一URL/重定向(Unique URLs / Redirection Behavior)
说明,这一小节没看懂。
Quickstart中举了下面两个例子:
@app.route('/projects/')
@app.route('/about')
其中,第一个以斜杠结束,第二个没有斜杠。
在访问第一个时,如果你没有加斜杠,那么,Flask会自动帮你跳转并添加;
而在访问第二个时,如果加了斜线,那么抱歉,404等着你。
好处是什么呢?
帮助保证这些资源的URL唯一性,而这又可以帮助避免搜索引擎把相同的页面做两次索引。
好吧,还是不太明白,但会是个好东西的。
3.URL构建(URL Building)
用处:给指定函数构建一个URL,使用url_for()函数。
此函数接收的第一个参数为函数名,而其它有一些数量的参数被当作关键词参数,每一个都对应着URL规则的变量部分。
还有一些不知道的变量部分被附加到URL后作为查询参数。
为何要这么做呢?而不是如前面讲的硬编码到函数模板中?
a.反转经常比硬编码有更多描述性东西;
b.开发者可以一口气改变URL,而不需要记得去手动更改硬编码的URL;
c.URL构建可以透明地处理特殊字符和Unicode字符的转义(escaping);
d.产生的路径总是绝对路径,这避免了在浏览器中使用相对路径可能发生的一些非预期错误;
e.如果你的应用放在了URL根目录之外,url_for()也可以正确的处理;(不明白,需要例子)
Quickstart中举了一个例子,感觉它就是把几个url_for()函数转换的结果打印出来了,可是,对于项目的请求、响应有什么用呢?
难道客户端只能使用url_for()转换的URL去访问?比如,那么,代码中的硬编码的例子还有效吗?
4.HTTP方法
route()函数的参数包括一个名为methods的,可以用来限制访问URL的请求方法(常见的请求访问有GET、POST,还有其它的)。
示例:
@app.route('/login', methods=['GET', 'POST'])
上面的示例近允许了GET、POST方法的请求访问,其它的就不可以了。
另外,GET方法允许后,HEAD方法自动就允许了。
--
静态文件(Static Files)
在生产环境,HTTP服务器可以处理静态文件;但在开发过程中,Flask也可以处理静态文件。
在package下建立一个名为static的文件夹,或者,和你的模块文件在同一级目录,然后,静态文件就可以以"/static"开头的URL进行访问了。
--
渲染模板(Rendering Templates)
Flask自动配置了Jinja2模板引擎。可以使用render_template()函数渲染一个模板。开发者唯一要做的是,传递模板文件名和关键词参数给render_template()函数。
示例:
1 from flask import render_template 2 3 @app.route('/hello/') 4 @app.route('/hello/<name>') 5 def hello(name=None): 6 return render_template('hello.html', name=name)
Flask会在templats文件夹中寻找模板文件,此文件夹和前面的static文件夹的位置相同,同级。
模板怎么写?常见的HTML、JavaScript、CSS外, 就是模板相关的变量、标签、流程控制、过滤器、模板继承、自动转义等,在Jinja2中还发现一个新的Markup类。
更多关于模板的内容,还是要看看Jinja2的文档。
--
访问请求数据(Accessing Request Data)
Flask中提供了 全局的request对象 用来保存客户端请求的数据。
为什么这个对象是全局的?Flask又是怎么做到线程安全的?哈,不懂,更不懂为何要提出这样的问题,好尴尬。
因为Flask有一个 本地化上下文功能(Context Locals)。
此小节包含四部分:
1.本地化上下文(下面主要是翻译Quickstart中的内容)
Flask中的一些对象是全局的,这些对象实际上是 代理,代理的是本地的到一些具体的上下文的对象。
把这些上下文想象成处理中的线程。一个请求近来,Flask决定新建一个线程。在Flask开始内部请求处理时,它清楚当前线程是 活跃的(active),并且
绑定到当前的应用和WSGI环境。
Flask再次用了一种智能的方法,因此,一个应用可以 不用中断 就发起(invoke)另一个应用。
这对开发者有什么用啊?基本上是可以忽略的,除非要做单元测试类的工作。你会注意到,那些依赖于request对象的代码可能突然 因为没有request对象 而被打断。
解决办法就是,创建你自己的 并 绑定到上下文中。
Quickstart中还有两个关于 解决方案 的例子,大家可以去看看。
1041.说实话,暂时没看明白这个。
2.Request对象
导入请求对象:
from flask import request
当前可用请求方法 通过method属性:
if request.method == 'POST':
访问表单数据可以使用form属性:
request.form['username']
request.form['password']
说明,表单数据如果不在form属性中怎么办?KeyError!可以抓取到它并处理,或者,返回一个400页面。
访问URL中提交的参数(?key=value)使用args属性:
searchwords = request.args.get('key', '')
Flask推荐使用get方法获取,因为其已经对KeyError进行了处理,否则发生错误时返回不有好的400页面。
更多关于request的内容,需要查看requst的文档。
3.文件上传
使用Flask处理文件上传很容易。
上传的文件被保存到 内存 或 文件系统中的某个临时位置。
开发者可以通过reqest对象的files属性查看这些上传的文件。
这些文件表现的就像是Python文件对象,当仍然有一个save()方法可以让开发者可以将它们保存到服务器的文件系统中。
例子:
1 from flask import request 2 3 @app.route('/upload', methods=['GET', 'POST']) 4 def upload_file(): 5 if request.method == 'POST': 6 f = request.files['the_file'] 7 f.save('/var/www/uploads/uploaded_file.txt')
想要知道文件在客户端的名字?可以使用filename属性。但是千万注意,这个filename属性可能会被伪造,若是你需要使用这个属性,请将它
传递给werkzeug模块的secure_filename()方法再用。
例子:
from flask import request from werkzeug.utils import secure_filename @app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': f = request.files['the_file'] f.save('/var/www/uploads/' + secure_filename(f.filename)) ...
更多关于文件上传的内容,可以查看Flask的文件上传模式文档。
4.Cookies
可以使用cookies属性访问cookies。
可以使用response对象的set_cookie方法设置cookie。
注意,若是你要使用sessions,那么,请不要直接使用Cookies。因为Flask中的sessions是在cookies的上面添加了一些安全机制的。
例子:读取cookies
username = request.cookies.get('username')
例子:存储cookies
1 from flask import make_response 2 3 @app.route('/') 4 def index(): 5 resp = make_response(render_template(...)) 6 resp.set_cookie('username', 'the username') 7 return resp
注意上面的make_response()函数的用法:这个方法用于创建你自己的response对象,并可以使用新建对象给返回结果添加一些 头部信息,当然,也包括设置cookies了。
--
重定向和错误(Redirects and Errors)
将用户重定向到另外的终端页面(endpoint),可以使用redirect()函数:
return redirect(url_for('login'))
P.S.看到了url_for()函数函数的用法,好开心。
提前终止请求并返回一个错误码,可以使用abort()函数:此函数调用后的代码就 不会被执行了
abort(401)
定制错误页面 请使用errorhandler()装饰器:
1 from flask import render_template 2 3 @app.errorhandler(404) 4 def page_not_found(error): 5 return render_template('page_not_found.html'), 404 # 不要忘记这里的404
--
关于响应(About Responses)
从一个视图函数返回的数据会被 自动转换成一个response对象。
Flask采用的逻辑如下:
a.如果response对象的类型正确,直接返回;
b.如果是字符串,使用这个数据和默认参数创建一个response对象;
c.如果是元组(tuple),元组的格式需要是(response, status, headers) 或者 (response, headers);P.S.这一段没翻译明白,也没弄太清楚
d.如果上面的都不是,Flask会假设返回的数据时一个有效的WSGI应用,并将之转换为一个response对象。
提示,如果开发者想要深入掌控response对象,可以使用make_response()方法。正如前面有提到——该header,用make_response()。
详见Quickstart中例子。
resp = make_response(render_template('error.html'), 404)
--
Sessions
和请求对象相关联的是一个叫做session的对象,这个对象用于记录 具体用户访问应用时的 一些信息(什么信息?)。
它是cookies上面实现的,并用加密的方式为cookies进行签名。这意味着,用户可以查看你的cookies的内容,但无法修改它,除非他们知道用于加密的密钥。
P.S.有什么用?签名后的cookies是怎么样的?看来,自己还是缺乏经验啊,
为了使用session,开发和需要设置应用的密钥(app.secret_key)(后文会讲怎么创建):
1 # Set the secret key to some random bytes. Keep this really secret! 2 app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' # 随机bytes
看来这里设置后,Flask的sessions就使用这个密码去给cookies前面了啊。
session的一些示例操作(下面的代码并非在一起的):
1 return 'Logged in as %s' % escape(session['username']) 2 ... 3 session['username'] = request.form['username'] 4 ... 5 # remove the username from the session if it's there 6 session.pop('username', None)
详见官网sessions文档。
怎么产生好的密钥(Hot to generate good secret keys)
执行下面的命令:
python -c "import os; print(os.urandom(16))"
注意,用双引号,而不是单引号!Quickstart中的是错误的!
注意,使用python3得到的和Quickstart中得到的不一样,不清楚怎么转换!
如下图:Python3转换出来哦,好像还能看的懂,Python2的就……,看来要限制产生的字符的范围,比如,只能是数字、字母、标点符号等。
注意:关于 基于cookies的sessions,Flask会获取你存储到sessions中的数据,并将它们序列化到cookie中。如果你发现某些数据在跨请求时没有
找到,那么,在确认cookies在客户端使能了,也没有获得明显错误信息时,请检查页面响应中的cookies的长度和Web浏览器支持的长度。
除了默认的基于客户端的sessions外,开发者还可以使用Flask的一些扩展模块去支持 服务器端sessions。
问题:基于客户端sessions、基于服务器端sessions,两者的区别是什么?分别有什么用?TBD
--
Message flashing
Flask使用flashing系统 给用户提供了一个简单的获取反馈的方法。
flashing系统基本上实现了 在请求最后记录一个信息,并在下一个请求仅仅是下一个请求时读取它。
这通常是和layout template联合使用以暴露信息。
使用flash()函数flash一个消息,使用get_flashed_messages()函数获取所有消息。
还不是太明白,,更多信息见官方文档。
--
Logging
浏览器访问不一定总是正确的,当错误、异常、恶意攻击等行为发生时,开发者应该记录相关问题。
这时,就可以使用Flask提供的logger了,从Flask 0.3版本就预配置好了。
Flask的logger是一个标准的日志记录器,可以到官网查看更多信息。
另外,关于应用可能发生的错误,请参考官网的应用错误文档。
--
Hooking in WSGI中间件
如果开发者想添加一个WSGI中间件到应用中,可以采用包装内部的WSGI应用。
P.S.不明白有什么用
--
使用Flask扩展
查找更多Flask扩展程序,请访问官网Extensions文档。
--
部署到一个Web服务器
--
后记:
总算写完了,昨天下午+昨天晚上一小时+今天10点开工到现在。
写完后,有什么进步吗?更牢靠了。可是,更熟练使用Flask了吗?
况且Quickstart文档中的不少内容都没整明白。
现在,要更进一步熟悉Flask需要:1.看更多文档、2.开发项目——在开发中学习。
还要多练习。
又是一篇长长的博文,也是醉了,感觉就是把Quickstart翻译了一遍,,至此,还有什么问题呢?
声明:
由于作者水平有限,如有错漏,请提醒通知。