Flask 成长之路(二)---- Flask的一个简单示例

上节我们已经安装好了 Flask ,接下来我们就利用 Flask 写一个最简单的示例。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def Hello():
    return 'Hello World~'
    

这个程序做了哪些事情呢:

  1. 首先导入了 Flask 类,这个类的实例将会成为我们的 WSGI 应用。
  2. 接下来我们创建了这个类的实例。传入的第一个参数是应用程序的模块名或者包名。__name__ 的值会因为启动的是应用程序(__main__)还是模块(模块名)会有所不同。Flask 需要这些信息来知道去哪里找到模板,静态文件等。
  3. 然后使用 Route() 装饰器来告诉 Flask 什么 URL 用来引发我们的函数。
  4. 函数名同样用来为特定的函数产生 URLs,函数用来返回我们想要在浏览器中展示的信息。

将上面的文件保存为 hello.py 或者其他的名字,除了 flask.py 因为这会和 Flask 自身的 flask.py 文件冲突。

运行这个应用,可以使用 flask 命令或者 python 的 -m 转换到 flask,在此之前,需要通过设置 FLASK_APP 将你的应用程序告诉它将要运行的终端。在 windows 中

set FLASK_APP = hello.py

接下来就可以使用 flask 命令或者 python 来启动应用

默认处于debugger模式,此模式能够让用户在电脑上运行任意的 python 代码,但是服务只能在本机上运行。如果要关闭 debugger 模式并让服务公开,只需要添加 flask run --host=0.0.0.0,他会让操作系统监听所有的公共 IP。

如果想要 debug 支持在代码改变的时候服务器自动重载而不是每次都手动重启服务,可以在运行服务之前设置 FLASK_ENV 为development,在 windows 中

set FLASK_ENV = development
flask run

她做了几件事:

  1. 激活 debugger
  2. 激活自动重载器
  3. 在 flask 应用中使能 debug 模式

也可以通过设置 FLASK_DEBUG = 1 来控制 debug 模式。

使用 route() 装饰器把一个函数绑定到 URL 上

@app.route('/')
def index():
    return 'Hello'

@app.route('/hello')
def hello():
    return 'Hello world'

还可以使用 <variable_name> 将变量部分加到 URL 中,函数可以接受这个变量作为参数。可以使用转换器来确定参数的类型,就像这样 <converter:variable_name>

@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('/path/<path:subpath>')
def show_subpath(subpath):
    return 'Subpath %s' % subpath

转换器一共有以下几种类型

需要注意的是,使用 path 这个转换器的时候,在变量名后面不加斜线,接收到的是不加斜线的参数;加斜线的时候,接收到的是加斜线的参数。但是对于不是 path 的转换器,如果路径中不是以斜线 '/' 结尾的话,而在搜索 URL 的时候加了斜线,就出现 400 Not Found 错误。这也是 URL 的 unique 特性,避免重复搜索。

看下面这2种写法。一种是在末尾加了斜线,一种是没有在末尾加上斜线。

@app.route('/projects/')
def projects():
    return 'The project page'

@app.route('/about')
def about():
    return 'The about page'

规范的 URL 写法后面是有斜线的。如果在搜索 URL 中没有加斜线,第一种写法会让 flask 重定向到标准的 URL,也就是会自动在搜索 URL 的时候后面加上斜线,从而在搜索的时候 URL 后面是否添加斜线都不会报错。但是第二种写法如果在搜索 about 页面的时候 URL 加了斜线,就会报 404 Not Found 错误,这也是 URL 的唯一性所决定的。

我们还可以用 url_for()函数来为某个函数建立 URL,他接受函数名作为它的第一个参数,并且可以接受任意数量的关键字参数,其中的每一个对应到 URL 规则的变量部分,未知的变量部分将会作为查询参数加入到 URL 。

使用 url_for()的理由有以下几个:

  1. 产生的路径是绝对的,能够避免相对路径的异常行为。
  2. 能够改变 URLs 而不用记住并手动改变 URLs

我们使用 test_request_context() 函数来试验 url_for() ,它让 Flask 在我们使用 Python shell 时像是处理一个请求一样进行动作

Web应用在搜索不同的 URL 使用不同的 HTTP 方法,默认的是,路径只会响应 GET 请求,你可以使用 route() 装饰器的 method 参数来处理不同的 HTTP 方法。

from flask import request

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        return do_the_login()
    else:
        return show_the_login_form()

动态 Web 应用同样需要静态文件,通常是 JavaScript 和 CSS 文件。要创建静态文件只需要在包里面创建一个文件并取名为 static ,就可以 /satatic 下使用。为静态文件创建 URL 可以使用 url_for() 函数,使用 static 这样一个特殊的端点名称。

url_for('static', filename='style.css')

这个文件必须在文件系统中存储为 static/style.css

Flask 使用为你的应用配置一个 Jinja模板引擎。使用 render_template() 函数来渲染一个模板,你需要提供模板名称以及传给模板引擎作为关键字参数的变量

from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)

Flask 会在 templates 文件中查找模板,因此如果你的应用是一个模块,这个文件就在模块旁边,如果是一个包它就会在包里

一个模板样例如下

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello, World!</h1>
{% endif %}

在模板里面你同样可以使用 request,session,和 g 对象,还有 get_flashed_messages() 函数。

request 对象记录在 API 部分,要使用首先要从 flask 模块中导入 request。

通过 request 的 method 属性可以获得当前请求的方法。通过 form 属性可以获得表单数据。

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'

    return render_template('login.html', error=error)

如果关键字在表单属性中不存在,就会抛出特殊的 KeyError 异常,可以像标准的 KeyError 异常一样对她进行捕获,否则页面就会显示 400 Bad Request。因此大多数情况不需要处理这个问题。

通过 args 属性获得传递到 URL 中的参数 (?key=value)

searchword = request.args.get('key', '')

推荐使用 get 或者捕获 KeyError 获得 URL 参数,因为用户可能改变 URL 或者不当的使用引发 400 Bad Request。request 对象更多的方法和属性可以查阅 Request 文档。

Flask 也可以很容易的处理上传的文件,这个时候需要在 HTML 表单中设置 'enctype="multipart/form-data' ,否则浏览器不会为你发送文件。通过 request 对象中的 file 属性获得这些文件,他就像标准的 python 文件对象一样,但是他有 save() 方法允许你把文件存储到服务器的文件系统。

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

 

如果想要知道文件在上传到应用之前在客户端是如何命名的,可以使用 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))

关于 Cookies ,可以 通过 cookies 属性获得 cookies 并可以通过 response 对象的 set_cookie 方法设置 cookies。request 对象的 cookies 属性是一个字典,包含了客户端发送的所有 cookies。如果想要使用 sessions,就不要直接使用 cookies 而是使用 Flask 中的 Sessions ,可以为 cookies 增加安全性。

from flask import request

@app.route('/')
def index():
    username = request.cookies.get('username')
    # use cookies.get(key) instead of cookies[key] to not get a
    # KeyError if the cookie is missing.

保存 cookies

from flask import make_response

@app.route('/')
def index():
    resp = make_response(render_template(...))
    resp.set_cookie('username', 'the username')
    return resp

通过 redirect() 可以重定向,使用 abort 加上错误码可以丢弃一个请求

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))

@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

可以使用 errorhandler() 装饰器来定制错误页

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

注意到 render_template 后面的 404 ,他会告诉 flask 那个页面的状态码,404代表 Not Found。200 代表正常。

视图函数的返回值会自动转换为 response 对象。如果返回值是一个字符串,它会转换为字符串作为响应体的 response 对象,或者是一个 200 OK 的状态码或者是一个 文本/HTML mimetype。Flask 转换返回值为 response 对象的逻辑如下:

  1. 如果返回正确的 response 对象类型,就直接从视图函数返回。
  2. 如果是一个字符串,就用那个数据和默认参数创建一个 response 对象。
  3. 如果返回一个元组,并且元组中的元素能够提供额外的信息,这样的元组必须放在表中(response,status,headers) 或者(response,headers) 也就是至少有一个元素放在表中。status 会重写状态码,headers 可以是一个 列表 或者 字典。
  4. 如果上面情况都没有出现,Flask 会认为返回值是一有效的 WSGI 应用,并将之转换为一个 response 对象。

如果想要控制 view 里面的 response 对象的结果,可以使用 make_response() 函数

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

例如上面的 view ,你只需要用 make_response()  重写返回表达式,获得 response 对象并修改它,然后将之返回即可。

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

除了 request 对象还有一种对象那就是 session 对象,它能够从一个请求到另一个请求的特定用户的信息。他以加密的方式在 cookies 上实现,这意味着用户能够看到你的 cookies 的内容但是不能修改它,除非以你知道密码。

要使用 sessions 你必须设置密码

from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'

@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 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'))

这里的 escape() 是用于当你没有使用模板引擎的时候跳出。

一个好的应用必须要处理好用户反馈,Flask 提供了一个简单的方式,也就是 flashing 系统,这个系统在请求最后记录信息,能够且仅能够在下一个请求获得它,这通常是和布局模板结合起来暴露这个信息。通常需要使用的函数就是 flash() 以及如果想要控制这个信息要使用的 get_flash_messages() 函数

如果想要记录日志信息,可以使用 logger

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

如果想要在应用中添加 WSGI 中间件,你也可以覆盖掉内置的 WSGI 应用

from werkzeug.contrib.fixers import LighttpdCGIRootFix
app.wsgi_app = LighttpdCGIRootFix(app.wsgi_app)

此外,Flask 还有许多扩展,这些扩展通常是一些包能够让你完成一些通用的任务。例如,Flask-SQLAlchemy 提供了 SQLAlchemy 简单易用的 Flask支持。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值