Flask 系列教程( 1)—URL 和视图

2017-07-20 16:30:16 +08:00
 CicadaMan

flask 简介:

flask是一款非常流行的Python Web框架,出生于 2010 年,作者是Armin Ronacher,本来这个项目只是作者在愚人节的一个玩笑,后来由于非常受欢迎,进而成为一个正式的项目。目前为止最新的版本是0.11.1

flask自 2010 年发布第一个版本以来,大受欢迎,深得开发者的喜爱,并且在多个公司已经得到了应用,flask 能如此流行的原因,可以分为以下几点:

Flask的灵活度非常之高,他不会帮你做太多的决策,即使做已经帮你做出选择,你也能非常容易的更换成你需要的,比如:

第一个 flask 程序:

pycharm新建一个flask项目,新建项目的截图如下:

点击create后创建一个新项目,然后在helloworld.py文件中书写代码:

#coding: utf8

# 从 flask 框架中导入 Flask 类
from flask import Flask

# 传入__name__初始化一个 Flask 实例
app = Flask(__name__)

# app.route 装饰器映射 URL 和执行的函数。这个设置将根 URL 映射到了 hello_world 函数上
@app.route('/')
def hello_world():
  return 'Hello World!'

if __name__ == '__main__':
# 运行本项目,host=0.0.0.0 可以让其他电脑也能访问到该网站,port 指定访问的端口。默认的 host 是 127.0.0.1,port 为 5000
app.run(host='0.0.0.0',port=9000)

然后点击运行,在浏览器中输入http://127.0.0.1:9000就能看到hello world了。需要说明一点的是,app.run这种方式只适合于开发,如果在生产环境中,应该使用Gunicorn或者uWSGI来启动。如果是在终端运行的,可以按ctrl+c来让服务停止。

设置为 DEBUG 模式:

默认情况下flask不会开启DEBUG模式,开启DEBUG模式后,flask 会在每次保存代码的时候自动的重新载入代码,并且如果代码有错误,会在终端进行提示。

开启DEBUG模式有三种方式:

  1. 直接在应用对象上设置:
app.debug = True
app.run()
  1. 在执行run方法的时候,传递参数进去:
app.run(debug=True)
  1. config属性中设置:
app.config.update(DEBUG=True)

如果一切正常,会在终端打印以下信息:

* Restarting with stat
* Debugger is active!
* Debugger pin code: 294-745-044
* Running on http://0.0.0.0:9000/ (Press CTRL+C to quit)

需要注意的是,只能在开发环境下开启DEBUG模式,因为DEBUG模式会带来非常大的安全隐患。

另外,在开启了DEBUG模式后,当程序有异常而进入错误堆栈模式,你第一次点击某个堆栈想查看变量值的时候,页面会弹出一个对话框,让你输入PIN值,这个PIN值在你启动的时候就会出现,比如在刚刚启动的项目中的PIN值为 294-745-044,你输入这个值后,Werkzeug会把这个PIN值作为cookie的一部分保存起来,并在 8 小时候过期,8 小时以内不需要再输入 PIN 值。这样做的目的是为了更加的安全,让调试模式下的攻击者更难攻击到本站。

项目配置:

Flask项目的配置,都是通过app.config对象来进行配置的。比如要配置一个项目处于DEBUG模式下,那么可以使用app.config['DEBUG] = True来进行设置,那么Flask项目将以DEBUG模式运行。在Flask项目中,有四种方式进行项目的配置:

  1. 直接硬编码:
app = Flask(__name__)
app.config['DEBUG'] = True
  1. 因为app.configflask.config.Config的实例,而Config类是继承自dict,因此可以通过update方法:
app.config.update(
  DEBUG=True,
   SECRET_KEY='...'
)
  1. 如果你的配置项特别多,你可以把所有的配置项都放在一个模块中,然后通过加载模块的方式进行配置,假设有一个settings.py模块,专门用来存储配置项的,此时你可以通过app.config.from_object()方法进行加载,并且该方法既可以接收模块的的字符串名称,也可以模块对象:
# 1. 通过模块字符串
app.config.from_object('settings')
# 2. 通过模块对象
import settings
app.config.from_object(settings)
  1. 也可以通过另外一个方法加载,该方法就是app.config.from_pyfile(),该方法传入一个文件名,通常是以.py结尾的文件,但也不限于只使用.py后缀的文件:
app.config.from_pyfile('settings.py',silent=True)
# silent=True 表示如果配置文件不存在的时候不抛出异常,默认是为 False,会抛出异常。

Flask项目内置了许多的配置项,所有的内置配置项,可以在这里查看

URL 与函数的映射:

从之前的helloworld.py文件中,我们已经看到,一个URL要与执行函数进行映射,使用的是@app.route装饰器。@app.route装饰器中,可以指定URL的规则来进行更加详细的映射,比如现在要映射一个文章详情的URL,文章详情的URL/article/id/,id 有可能为 1、2、3...,那么可以通过以下方式:

@app.route('/article/<id>/')
def article(id):
  return '%s article detail' % id

其中<id>,尖括号是固定写法,语法为<variable_name>variable_name默认的数据类型是字符串。如果需要指定类型,则要写成<converter:variable_name>,其中converter就是类型名称,可以有以下几种:

@app.route('/<any(article,blog):url_path>/')
def item(url_path):
  return url_path

以上例子中,item 这个函数可以接受两个URL,一个是/article/,另一个是/blog/。并且,一定要传url_path参数,当然这个url_path的名称可以随便。

如果不想定制子路径来传递参数,也可以通过传统的?=的形式来传递参数,例如:/article?id=xxx,这种情况下,可以通过request.args.get('id')来获取id的值。如果是post方法,则可以通过request.form.get('id')来进行获取。

构造 URL ( url_for ):

一般我们通过一个URL就可以执行到某一个函数。如果反过来,我们知道一个函数,怎么去获得这个URL呢?url_for 函数就可以帮我们实现这个功能。url_for()函数接收两个及以上的参数,他接收函数名作为第一个参数,接收对应URL 规则的命名参数,如果还出现其他的参数,则会添加到URL的后面作为查询参数

通过构建URL的方式而选择直接在代码中拼URL的原因有两点:

  1. 将来如果修改了URL,但没有修改该URL对应的函数名,就不用到处去替换URL了。
  2. url_for()函数会转义特殊字符和Unocode 数据,这些工作都不需要我们自己处理。

下面用一个例子来进行解释:

from flask import Flask,url_for
app = Flask(__name__)

@app.route('/article/<id>/')
def article(id):
  return '%s article detail' % id

# 这行的代码可以在交互模式下产生请求上下文,不用`app.run()`来运行这个项目,直接可以运行下面的代码,
# 也会有`flask`上下文
with app.test_request_context():
  print url_for('article',id='1')
  print url_for('article',id='2',next='/')
执行后的结果如下:
> /article/1/
> /article/2/?next=%2F

自定义 URL 转换器:

刚刚在 URL 映射的时候,我们看到了Flask内置了几种数据类型的转换器,比如有int/string等。如果Flask内置的转换器不能满足你的需求,此时你可以自定义转换器。自定义转换器,需要满足以下几个条件:

  1. 转换器是一个类,且必须继承自werkzeug.routing.BaseConverter
  2. 在转换器类中,必须实现to_python(self,value)方法,这个方法的返回值,将会传递到view函数中作为参数。
  3. 在转换器类中,必须实现to_url(self,values)方法,这个方法的返回值,将会在调用url_for函数的时候生成符合要求的URL形式。

比如,拿一个官方的例子来说,Reddit可以通过在URL中用一个加号(+)隔开社区的名字,方便同时查看来自多个社区的帖子。比如访问“ www.reddit.com/r/flask+lisp/”的时候,就同时可以查看 flask 和 lisp 两个社区的帖子,现在我们自定义一个转换器来实现这个功能:

#coding: utf8
from flask import Flask,url_for
from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):
  def __init__(self,url_map,separator='+'):
    super(ListConverter,self).__init__(url_map)
    self.separator = separator

def to_python(self, value):
  return value.split(self.separator)

def to_url(self, values):
  return self.separator.join(BaseConverter.to_url(self,value) for value in values)

app.url_map.converters['list'] = ListConverter

@app.route('/community1/<list:page_names>')
def community1(page_names):
  return '%s+%s' % tuple(page_names)

@app.route('/community2/<list('|'):page_names>/')
def community2(page_names):
  return "%s|%s" % tuple(page_names)

communityu1使用的是默认的+号进行连接,而第二种方式使用了|进行连接。

URL 唯一:

FlaskURL规则是基于Werkzeug的路由模块。这个模块的思想是基于Apache以及更早的HTTP服务器的主张,希望保证优雅且唯一的URL

举个例子:

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

上述例子中,当访问一个结尾不带斜线的URL会被重定向到带斜线的URL上去。这样有助于避免搜索引擎搜索同一个页面两次。

再看一个例子:

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

以上例子中,当访问带斜线的URL(/about/)会产生一个 404 ("Not Found")错误。

指定 HTTP 方法:

@app.route()中可以传入一个关键字参数methods来指定本方法支持的HTTP方法,默认只响应GET请求,看以下例子:

@app.route('/login/',methods=['GET','POST'])
def login():
  return 'login'

以上装饰器将让loginURL既能支持GET又能支持POST

页面跳转和重定向:

重定向分为永久性重定向和暂时性重定向,在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,因此我们应该给他重定向到登录页面。

flask中,重定向是通过flask.redirect(location,code=302)这个函数来实现的,location表示需要重定向到的URL,应该配合之前讲的url_for()函数来使用,code表示采用哪个重定向,默认是302也即暂时性重定向,可以修改成301来实现永久性重定向。

以下来看一个例子,关于在flask中怎么使用重定向:

from flask import Flask,url_for,redirect

app = Flask(__name__)
app.debug = True

@app.route('/login/',methods=['GET','POST'])
def login():
  return 'login page'

@app.route('/profile/',methods=['GET','POST'])
def profile():
  name = request.args.get('name')

if not name:
# 如果没有 name,说明没有登录,重定向到登录页面
  return redirect()
else:
  return name

关于响应( Response ):

视图函数的返回值会被自动转换为一个响应对象,Flask的转换逻辑如下:

以下将用例子来进行说明:

第一个例子:直接使用Response创建:

from werkzeug.wrappers import Response

@app.route('/about/')
def about():
  resp = Response(response='about page',status=200,content_type='text/html;charset=utf-8')
  return resp

第二个例子:可以使用make_response函数来创建Response对象,这个方法可以设置额外的数据,比如设置 cookie,header 信息等:

from flask import make_response

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

第三个例子:通过返回元组的形式:

@app.errorhandler(404)
def not_found():
  return 'not found',404

第四个例子:自定义响应。自定义响应必须满足三个条件:

以下将用一个例子来进行讲解,Restful API都是通过JSON的形式进行传递,如果你的后台跟前台进行交互,所有的URL都是发送JSON数据,那么此时你可以自定义一个叫做JSONResponse的类来代替Flask自带的Response类:

from flask import Flask,jsonify
from werkzeug.wrappers import Response

app = Flask(__name__)

class JSONResponse(Response):
default_mimetype = 'application/json'

@classmethod
def force_type(cls,response,environ=None):
  if isinstance(response,dict):
    response = jsonify(response)
    return super(JSONResponse,cls).force_type(response,environ)

app.response_class = JSONResponse

@app.route('/about/')
def about():
  return {"message":"about page"}

if __name__ == '__main__':
  app.run(host='0.0.0.0',port=8000)

此时如果你访问/about/这个URL,那么在页面中将会显示:

{
"message": "about page"
}

注意以上例子,如果不写app.response_class = JSONResponse,将不能正确的将字典返回给客户端。因为字典不在Flask的响应类型支持范围中,那么将调用app.response_class这个属性的force_type类方法,而app.response_class的默认值为Response,因此会调用Response.force_class()这个类方法,他有一个默认的算法转换成字符串,但是这个算法不能满足我们的需求。因此,我们要设置app.response_class=JSONResponse,然后重写JSONResponse中的force_type类方法,在这个方法中将字典转换成JSON格式的字符串后再返回。

对 Flask 和 Python 感兴趣的,可以加群:482869582。一起讨论和学习哦~~

5011 次点击
所在节点    Flask
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/376733

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX