1 初识Flask


1.1 搭建开发环境


  1. 用pip+virtualenv管理包和虚拟环境
  2. 这里个人推荐使用conda环境和包管理工具(MiniConda)
  3. Pipenv
    一个程序通常会使用很多的Python包,即依赖(dependency)。而程序不仅仅会在一台电脑上运行,程序部署上线时需要安装到远程服务 器上,而你也许会把它分享给朋友。如果你打算开源的话,就可能会有 更多的人需要在他们的电脑上运行。为了能顺利运行程序,他们不得不记下所有依赖包,然后使用pip、conda或Pipenv安装,这些重复无用的工作当然应该避免。在以前我们通常使用pip搭配一个requirements.txt文件来记录依赖。但requirements.txt需要手动维护,在使用上不够灵活。Pipfile的出现就是为了替代难于管理的requirements.txt。

安装flask,这些包管理工具同时也会安装flask的相关依赖包


1.2.1 创建程序实例


以下app.py脚本中包含一个最小的Flask程序
对于简单的的程序来说,程序的主模块一般命令为app.py。你也可以 使用其他名称,比如hello.py,但是要避免使用flask.py,因为这和Flask 本身冲突。

#app.py
from flask import Flask 
app = Flask(__name__)  #创建Flask程序实例
#Flask类是Flask的核心类,它提供了很多与程序相关的属性和方法。

@app.route('/') 
def index():    
       return '<h1>Hello Flask!</h1>'

传入Flask类构造方法的第一个参数是模块或包的名称,我们应该使用特殊变量name。Python会根据所处的模块来赋予name变量相应的值,对于我们的程序来说(app.py),这个值为app。除此之外,这也会帮助Flask在相应的文件夹里找到需要的资源,比如模板和静态文件。

1.2.2 注册路由

在一个Web应用里,客户端和服务器上的Flask程序的交互可以简单 概括为以下几步:
1)用户在浏览器输入URL访问某个资源。
2)Flask接收用户请求并分析请求的URL。
3)为这个URL找到对应的处理函数。
4)执行函数并生成响应,返回给浏览器。
5)浏览器接收并解析响应,将信息显示在页面中。

上面这些步骤大部分都由Flask完成,我们要做的是建立处理请求的函数,并为其定义对应的URL规则。只需为函数附加 app.route()装饰器,并传入URL规则作为参数,我们就可以让URL与函数建立关联。这个过程我们称为注册路由(route),路由负责管理 URL和函数之间的映射,而这个函数则被称为视图函数(view function)。

@app.route('/') 
def index():    
    return '<h1>Hello, World!</h1>'

在这个程序里,app.route()装饰器把根地址/和index()函数绑定起来,当用户访问这个URL时就会触发index()函数。这个视图函数可以像其他普通函数一样执行任意操作,比如从数据库中获取信息, 获取请求信息,对用户输入的数据进行计算和处理等。最后,视图函数 返回的值将作为响应的主体,一般来说,响应的主体就是呈现在浏览器窗口的HTML页面。

虽然这个程序相当简单,但它却是大部分Flask程序的基本模式。在复杂的程序中,我们会有许多个视图函数分别处理不同URL的请求,在视图函数中会完成更多的工作,并且返回包含各种链接、表单、图片的 HTML文件,而不仅仅是一行字符串。返回的页面中的链接又会指向其 他URL,被单击后触发对应的视图函数,获得不同的返回值,从而显示 不同的页面,这就是我们浏览网页时的体验。

route()装饰器的第一个参数是URL规则,用字符串表示,必须以 斜杠(/)开始。这里的URL是相对URL(又称为内部URL),即不包含域名的URL

1 为视图绑定多个URL:
一个视图函数可以绑定多个URL,比如下面的代码把/hi和/hello都 绑定到say_hello()函数上,这就会为say_hello视图注册两个路由,用 户访问这两个URL均会触发say_hello()函数,获得相同的响应

@app.route('/hi') 
@app.route('/hello') 
def say_hello():    
    return '<h1>Hello, Flask!</h1>'

**2 动态URL **

我们不仅可以为视图函数绑定多个URL,还可以在URL规则中添加变量部分,使用“<变量名>”的形式表示。Flask处理请求时会把变量传入视图函数,所以我们可以添加参数获取这个变量值

@app.route('/greet/<name>') 
def greet(name):    
    return '<h1>Hello, %s!</h1>' % name
  《多个参数怎么操作呢???》

因为URL中可以包含变量,所以我们将传入app.route()的字符串称为URL规则而不是URL。Flask会解析请求并把请求的URL与视图函数的URL规则进行匹配。比如,这个greet视图的URL规则 为/greet/<name>,那么类似/greet/foo、/greet/bar的请求都会触发这个视 图函数。

当URL规则中包含变量时,如果用户访问的URL中没有添加变量, 比如/greet,那么Flask在匹配失败后会返回一个404错误响应。一个很常见的行为是在app.route()装饰器里使用defaults参数设置URL变量的默 认值,这个参数接收字典作为输入,存储URL变量和默认值的映射。在下面的代码中,我们为greet视图新添加了一个app.route()装饰器,

  @app.route('/greet', defaults={'name': 'Programmer'}) 
  @app.route('/greet/<name>') 
  def greet(name):    
    return '<h1>Hello, %s!</h1>' % name

这时如果用户访问/greet,那么变量name会使用默认值 Programmer,视图函数返回<h1>Hello,Programmer!</h1>。上面的用 法实际效果等同于:

@app.route('/greet') 
@app.route('/greet/<name>') 
def greet(name='Programmer'):   
    return '<h1>Hello, %s!</h1>' % name

1.3 启动开发服务器


Flask内置了一个简单的开发服务器(由依赖包Werkzeug提供), 足够在开发和测试阶段使用。在生产环境需要使用性能够好的生产服务器,以提升安全和性能。

Flask通过依赖包Click内置了一个CLI(Command Line Interface,命 令行交互界面)系统。当我们安装Flask后,会自动添加一个flask命令脚本,我们可以通过flask命令执行内置命令、扩展提供的命令或是我们自己定义的命令。其中,flask run命令用来启动内置的开发服务器。启动时确保执行命令前激活了虚拟环境。

flask run命令运行的开发服务器默认会监听http://127.0.0.1:5000/地址(按Crtl+C退出),并开启多线程支持。http://localhost::5000/与http://127.0.0.1:5000/除了地址不同外,两者没有实际区别,即域名和IP地址的映射关系。

旧的启动开发服务器的方式是使用app.run()方法,目前已不推荐 使用(deprecated)。

一般来说,在执行flask run命令运行程序前,我们需要提供程序实例所在模块的位置。我们在上面可以直接运行程序,是因为Flask会自动 探测程序实例,自动探测存在下面这些规则:
·从当前目录寻找app.py和wsgi.py模块,并从中寻找名为app或application的程序实例。
·从环境变量FLASK_APP对应的值寻找名为app或application的程序实例。

因为我们的程序主模块命名为app.py,所以flask run命令会自动在 其中寻找程序实例。如果你的程序主模块是其他名称,比如hello.py, 那么需要设置环境变量FLASK_APP,将包含程序实例的模块名赋值给这个变量。

#Linux或macOS系统使用export命令
 export FLASK_APP=hello
 
 #在Windows系统中使用set命令:
set FLASK_APP=hello

我们在上面启动的Web服务器默认是对外不可见的,可以在run命 令后添加--host选项将主机地址设为0.0.0.0使其对外可见:

 flask run --host=0.0.0.0

这会让服务器监听所有外部请求。个人计算机(主机)一般没有公 网IP(公有地址),所以你的程序只能被局域网内的其他用户通过你的 个人计算机的内网IP(私有地址)访问,比如你的内网IP为 192.168.191.1。当局域网内的其他用户访问http://192.168.191.1:5000时, 也会看到浏览器里显示一行“Hello,Flask!”。要让互联网上的所有人都可访问,可以考虑使用ngrok(https://ngrok.com/)Localtunnel(https://localtunnel.github.io/www/)等内网穿透/端口转发工 具。

Flask提供的Web服务器默认监听5000端口,你可以在启动时传入参数来改变它:

 flask run --port=8000

执行flask run命令时的host和port选项也可以通过环境变量 FLASK_RUN_HOST和FLASK_RUN_PORT设置。事实上,Flask内置的
命令都可以使用这种模式定义默认选项值, 即“FLASK_<COMMAND>_<OPTION>”,你可以使用flask--help命令查 看所有可用的命令。

** 设置运行环境**

开发环境是指我们在本地编写和测试程序时的计算机环境,而生产环境与开发环境相对,它指的是网站部署上线供用户访问时的服务器环境。
根据运行环境的不同,Flask程序、扩展以及其他程序会改变相应的行为和设置。为了区分程序运行环境,Flask提供了一个FLASK_ENV环境变量用来设置环境,默认为production(生产)。在开发时,我们可 以将其设为development(开发),这会开启所有支持开发的特性

在开发环境下,调试模式(Debug Mode)将被开启,这时执行flask run启动程序会自动激活Werkzeug内置的调试器(debugger)和重载器 (reloader),它们会为开发带来很大的帮助。如果你想单独控制调试模式的开关,可以通过FLASK_DEBUG环境 变量设置,设为1则开启,设为0则关闭,不过通常不推荐手动设置这个
值。

在生产环境中部署程序时,绝不能开启调试模式。尽管PIN码可以 避免用户任意执行代码,提高攻击者利用调试器的难度,但并不能确保 调试器完全安全,会带来巨大的安全隐患。而且攻击者可能会通过调试 信息获取你的数据库结构等容易带来安全问题的信息。另一方面,调试 界面显示的错误信息也会让普通用户感到困惑。

1.调试器
Werkzeug提供的调试器非常强大,当程序出错时,我们可以在网页 上看到详细的错误追踪信息,这在调试错误时非常有用。

2.重载器
当我们对代码做了修改后,期望的行为是这些改动立刻作用到程序 上。重载器的作用就是监测文件变动,然后重新启动开发服务器。默认会使用Werkzeug内置的stat重载器,它的缺点是耗电较严重, 而且准确性一般。为了获得更优秀的体验,我们可以安装另一个用于监 测文件变动的Python库Watchdog,安装后Werkzeug会自动使用它来监测 文件变动

此外如果项目中使用了单独的CSS或JavaScript文件时,那么浏览器可能会缓存这些文件,从而导致对文件做出的修改不能立刻生效。在浏览器中,我们可以按下Crtl+F5或Shift+F5执行硬重载(hard reload),即忽略缓存并重载(刷新)页面。


1.5 Flask扩展


我们将会接触到很多Flask扩展。扩展(extension)即使用 Flask提供的API接口编写的Python库,可以为Flask程序添加各种各样的功能。大部分Flask扩展用来集成其他库,作为Flask和其他库之间的薄薄一层胶水。因为Flask扩展的编写有一些约定,所以初始化的过程大致相似。大部分扩展都会提供一个扩展类,实例化这个类,并传入我们创建的程序实例app作为参数,即可完成初始化过程。通常,扩展会在传 入的程序实例上注册一些处理函数,并加载一些配置。

在日常开发中,大多数情况下,我们没有必要重复制造轮子,所以选用扩展可以避免让项目变得臃肿和复杂。尽管使用扩展可以简化操作,快速集成某个功能,但同时也会降低灵活性。如果过度使用扩展, 在不需要的地方引入,那么相应也会导致代码不容易维护。更糟糕的是,质量差的扩展可能还会带来潜在的Bug,而不同扩展之间也可能会出现冲突。因此,在编写程序时,应该尽量从实际需求出发,只在需要的时候使用扩展,并把扩展的质量和兼容性作为考虑因素,尽量在效率和灵活性之间达到平衡。


1.6 项目配置


在很多情况下,你需要设置程序的某些行为,这时你就需要使用配置变量。在Flask中,配置变量就是一些大写形式的Python变量,你也可 以称之为配置参数或配置键。使用统一的配置变量可以避免在程序中以硬编码(hard coded)的形式设置程序。

在一个项目中,你会用到许多配置:Flask提供的配置,扩展提供的配置,还有程序特定的配置。和平时使用变量不同,这些配置变量都通过Flask对象的app.config属性作为统一的接口来设置和获取,它指向的 Config类实际上是字典的子类,所以你可以像操作其他字典一样操作 它。
Flask提供了很多种方式来加载配置。比如,你可以像在字典中添加 一个键值对一样来设置一个配置:

app.config['ADMIN_NAME'] = 'Peter'
#配置的名称必须是全大写形式,小写的变量将不会被读取。

使用update()方法则可以一次加载多个值:
app.config.update(    TESTING=True, 
                            SECRET_KEY='_5#yF4Q8z\n\xec]/' )

除此之外,你还可以把配置变量存储在单独的Python脚本、JSON 格式的文件或是Python类中,config对象提供了相应的方法来导入配 置,具体我们会在后面了解。

#和操作字典一样,读取一个配置就是从config字典里通过将配置变 量的名称作为键读取对应的值:
value = app.config['ADMIN_NAME']


1.7 URL与端点


在Web程序中,URL无处不在。如果程序中的URL都是以硬编码的方式写出,那么将会大大降低代码的易用性。比如,当你修改了某个路由的URL规则,那么程序里对应的URL都要一个一个进行修改。更好的解决办法是使用Flask提供的url_for()函数获取URL,当路由中定义的 URL规则被修改时,这个函数总会返回正确的URL。

调用url_for()函数时,第一个参数为端点(endpoint)值。在Flask中,端点用来标记一个视图函数以及对应的URL规则。端点的默认值为视图函数的名称。

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

这个路由的端点即视图函数的名称index,调用url_for('index')即 可获取对应的URL,即“/”。

如果URL含有动态部分,那么我们需要在url_for()函数里传入相 应的参数,以下面的视图函数为例:

@app.route('/hello/<name>') 
def greet(name):    
    return 'Hello %s!' % name

这时使用url_for('say_hello',name='Jack')得到的URL为“/hello/Jack”。

我们使用url_for()函数生成的URL是相对URL(即内部URL), 即URL中的path部分,比如“/hello”,不包含根URL。相对URL只能在程序内部使用。如果你想要生成供外部使用的绝对URL,可以在使用 url_for()函数时,将_external参数设为True,这会生成完整的URL,在本地运行程序时则会获得 http://localhost:5000/hello


1.8 Flask命令


除了Flask内置的flask run等命令,我们也可以自定义命令。在虚拟环境安装Flask后,包含许多内置命令的flask脚本就可以使用了。在前面 我们已经接触了很多flask命令,比如运行服务器的flask run,启动shell 的flask shell。

通过创建任意一个函数,并为其添加app.cli.command()装饰器, 我们就可以注册一个flask命令。下面创建了一个自定义的 hello()命令函数,在函数中我们仍然只是打印一行问候。

#创建自定义命令

@app.cli.command() 
def hello():   
    click.echo('Hello, Human!')

函数的名称即为命令名称,这里注册的命令即hello,你可以使用 flask hello命令来触发函数。作为替代,你也可以在app.cli.command() 装饰器中传入参数来设置命令名称,比如app.cli.command('say-hello') 会把命令名称设置为say-hello,完整的命令即flask say-hello。


1.9 模板与静态文件


一个完整的网站需要使用模板(template)和静态文件(static file)来生成内容丰富的网页。 模板即包含程序页面的HTML文件,静态文件则是需要在HTML文件中加载的CSS、JavaScript文件,以及图片、字体文件等资源文件。默认情况下,模板文件存放在项目根目录中的templates文件夹中,静态文件存放在static文件夹下,这两个文件夹需要和包含程序实例的模块处于同一个目录下,对应的项目结构示例如下所示:

hello/   
    - templates/   
    - static/    
    - app.py

在开发Flask程序时,使用CSS框架和JavaScript库是很常见的需求,一般会直接从CDN加载资源或者下载至本地作为本地资源使用。建议在开发环境下自己下载到static目录下使用本地资源,这样可以提高加载速度,方便统一管理,出于方便的考虑也可以使用扩展内置的本地资源。在过渡到生产环境时,自己手动管理所有本地资源或自己设置CDN,避免使用扩展内置的资源。这个建议主要基于如下考虑因素:
·鉴于国内的网络状况,扩展默认使用的国外CDN可能会无法访问或访问过慢。
·不同扩展内置的加载方法可能会加载重复的依赖资源,比如 jQuery。
·在生产环境下,将静态文件集中在一起更方便管理。
·扩展内置的资源可能会出现版本过旧的情况。
·也存在资源链接失效的情况

CDN指分布式服务器系统。服务商把你需要的资源存储在分布于不 同地理位置的多个服务器,它会根据用户的地理位置来就近分配服务器 提供服务(服务器越近,资源传送就越快)。使用CDN服务可以加快网页资源的加载速度,从而优化用户体验。对于开源的CSS和JavaScript 库,CDN提供商通常会免费提供服务。


1.10 Flask与MVC架构


MVC架构最初是用来设计桌面程序的,后来也被用于Web程序,应 用了这种架构的Web框架有Django、Ruby on Rails等。在MVC架构中, 程序被分为三个组件:数据处理(Model)、用户界面(View)、交互逻辑(Controller)。如果套用MVC架构的内容,那么Flask中视图函数的名称其实并不严谨,使用控制器函数(Controller Function)似乎更合适些,虽然它也附带处理用户界面。严格来说,Flask并不是MVC架构的框架,因为它没有内置数据模型支持。
粗略归类,如果想要使用Flask来编写一个MVC架构的程序,那么视图函数可以作为控制器(Controller),视图(View)则是使用Jinja2渲染的HTML模板,而模型(Model)可以使用其他库来实现,比如SQLAlchemy来创建数据库模型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350