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服务器

请查看官方文档Deployment Options

 

--

后记:

总算写完了,昨天下午+昨天晚上一小时+今天10点开工到现在。

写完后,有什么进步吗?更牢靠了。可是,更熟练使用Flask了吗?

况且Quickstart文档中的不少内容都没整明白。

现在,要更进一步熟悉Flask需要:1.看更多文档、2.开发项目——在开发中学习。

还要多练习。

 

又是一篇长长的博文,也是醉了,感觉就是把Quickstart翻译了一遍,,至此,还有什么问题呢?

 

声明:

由于作者水平有限,如有错漏,请提醒通知。

 

posted @ 2018-05-18 13:46  快乐的凡人721  阅读(622)  评论(0编辑  收藏  举报