概述

Flask是一个用Python编写的Web应用程序框架。 它由 Armin Ronacher 开发,他领导一个名为Pocco的国际Python爱好者团队。 Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。两者都是Pocco项目。

Flask中文文档

环境

Python2.7及以上,Python3.4及以上,我这里用的Python2.7

安装virtualenv

virtualenv可以创建一个独立的python运行环境,这个环境和系统的python环境是互不干扰的,也就是说你在这个环境中安装的python包不会安装到系统的python环境中,系统python环境中的包会复制到这个环境中(但新版本的virtualenv并不会复制过来,默认只安装setuptoolswheelpip

当然如果不想用系统的包的话可以加一些参数:

–-no-site-packages:表示在建立虚拟环境时不将原版本中的第三方库拷贝过来,这样就能获得一个纯净的Python环境。(可这个参数新版的virtualenv好像用不了,我的就是这样,但网上都这样讲,=.=)

--no-setuptools:不安装setuptools;--no-wheel:不安装whell;--no-pip:不安装pip

安装virtualenv包:

pip install virtualenv

创建环境:

mkdir Flask  #创建项目目录
cd Flask
virtualenv -p C:\Python2\python.exe E:\flask\venv  #在这个项目中创建一个独立的python环境,环境命名为venv,-p表示指定python的版本路径
.\venv\Scripts\activate  #启动虚拟环境;deactivate可退出此环境

接下来就要在这个虚拟环境中安装Flask了

E:\flask\venv\Scripts\python.exe -m pip list  #可看到当前虚拟环境中的所有的python包,当然不加路径也可以
pip install Flask

至此虚拟环境就搭建成功了,当然如果你不想用虚拟的python环境用系统自带的也可以

应用

from flask import Flask #导入Flask模块
app = Flask(__name__)#使用当前模块作为参数
@app.route('/')
def hello_world():
    return 'Hello World'

if __name__ == "__main__":
    app.run('0.0.0.0',debug = True)

app.route(rule, options):该函数为Flask类的一个方法,告诉应用程序哪个URL应该调用相关的函数

  • rule:参数表示与该函数的URL绑定
  • endpoint:被注册的url的名字,一般用来反向生成url的时候使用,默认把视图函数的名字作为endpoint,如:endpoint=”login”
  • options:是要转发给基础Rule对象的参数列表。

上面的'/'表示URL与hello_world函数绑定,在浏览器中打开web服务器的主页时,将呈现该函数的输出

app.run(host, port, debug, options):表示本地开发服务器上运行应用程序

  • host:要监听的主机名。 默认为127.0.0.1(localhost)。设置为“0.0.0.0”以使服务器在外部可用
  • port:端口设置,默认值为5000
  • debug:默认为false。 如果设置为true,则提供调试信息
  • options:要转发到底层的Werkzeug服务器。

浏览器访问http://127.0.0.1:5000/即可看到函数的输出

路由

from flask import Flask
app = Flask(__name__)
#@app.route('/hello')  #添加路由
def hello_world():
    return 'Hello World'
app.add_url_rule('/hello/','hello',view_func=hello_world)
if __name__ == "__main__":
    app.run('0.0.0.0',debug = True)

app.add_url_rule(rule,endpoint,view_func,options):该方法与route类似

  • rule:一个字符串格式的url规则,如:”/login”
  • endpoint:url规则的名字,用来反向生成url使用,默认是视图函数的名字。
  • view_func:视图函数,当对应的endpoint名字被请求时需要调用的函数。
  • options: 类似route时候的options,methods参数默认是只监听get

变量规则

from flask import Flask
app = Flask(__name__)
@app.route('/hello/<name>')
def hello_world(name):
    return 'Hello { }'.format(name)

if __name__ == "__main__":
    app.run('0.0.0.0',debug = True)

在浏览器输入http://127.0.0.1:5000/hello/flask则会显示Hello flask

除了默认字符串变量部分之外,还可以使用以下转换器构建规则:

  • int:接受整数,不为整数则404,@app.route('/hello/<int:name>')
  • float:接受浮点数,不为浮点数则404,@app.route('/hello/<float:name>')
  • path:接受用作目录分隔符的斜杠,@app.route('/hello/<path:name>')

URL构建

from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/name')
def hello_admin():
   return 'hello Admin'
@app.route('/guest/<guest>')
def hello_guest(guest):
   return 'Hello { } as Guest'.format(guest)
@app.route('/user/<name>')
def hello_user(name):
   if name == 'admin':
       return redirect(url_for('hello_admin'))#redirect函数直接对route的路径进行访问,url_for直接对函数进行访问,一同使用则访问此路径下的函数
   else:
       return redirect(url_for('hello_guest',guest = name))
       
if __name__ == '__main__':
   app.run(debug = True)

redirect(location, code=302, Response=None):该函数用来实现重定向功能

  • location:一个链接地址,可以使用url_for()函数得到,也可以是静态文件地址
  • code:可以取值为301、302、303、305、307,默认302,300、304不可以
  • Response:一个响应类,默认是werkzeug.wrappers.Response

输入http://127.0.0.1:5000/user/admin则会302重定向到http://127.0.0.1:5000/name显示hello Admin

http://127.0.0.1:5000/user/flask则会302重定向到http://127.0.0.1:5000/guest/flask,显示Hello flask as Guest

HTTP方法

首先创建一个HTML表单,使用POST方法将表单数据发送到URL,命名为login.html

<html>
   <body>
      <form action = "http://localhost:5000/login" method = "post">
         <p>Enter Name:</p>
         <p><input type = "text" name = "nm" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
   </body>
</html>
from flask import Flask, redirect, url_for, request
app = Flask(__name__)
@app.route('/success/<name>')
def success(name):
   return 'welcome { }'.format(name)
@app.route('/login',methods = ['POST','GET'])
def login():
   if request.method == 'POST':
       user = request.form['nm']  #获取参数nm的值
       return redirect(url_for('success',name = user))
   else:
       user = request.args.get('nm')
       return redirect(url_for('success',name = user))

if __name__ == '__main__':
   app.run(debug = True)

Request对象

  • Form:是一个字典对象,包含表单参数及其值的键和值对。
  • args:解析查询字符串的内容,是包含表单参数对及其对应值对的列表的字典对象
  • Cookies:保存Cookie名称和值的字典对象。
  • files:与上传文件有关的数据。
  • method:当前请求方法。

输入flask回车后先跳转到http://127.0.0.1:5000/login然后自动POST一个nm参数后302重定向到http://127.0.0.1:5000/success/flask输出welcome flask

模板

from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
   return '<h1>Hello World</h1>'
if __name__ == '__main__':
   app.run(debug = True)

访问网站会得到一个Hello World标题,但这样吧标签插入在python代码中就很麻烦,于是这里就引入了Jinja2模板引擎,可以通过render_template()函数呈现HTML文件。

新建一个templates文件夹,在其中建立一个hello.html文件:

<!doctype html>
  <h1>Hello { {  marks }}!</h1>
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/')
@app.route('/hello/<score>')
def hello_name(score=None):
   return render_template('hello.html',marks = score)#自动寻找目录下的templates文件夹下的html文件
if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

访问http://127.0.0.1:5000/hello/flask则会输出Hello flask!标题

Jinja2模板引擎使用以下分隔符从HTML转义。

  • {% ... %}:用于语句
  • {{ ... }}:用于表达式可以打印到模板输出
  • ``:用于未包含在模板输出中的注释
  • # ... ##:用于行语句

另外测试语句效果,修改hello.html为以下内容

<!doctype html>
{% if marks>50 %}
<h1> Your result is pass!</h1>
{% else %}
<h1>Your result is fail</h1>
{% endif %}

脚本中@app.route('/hello/<score>')修改为@app.route('/hello/<int:score>')

则当访问http://127.0.0.1:5000/hello/1时输出Your result is fail

则当访问http://127.0.0.1:5000/hello/100时输出Your result is pass!

注意这里必须要对传入的score定义为int型,否则会被当成字符串处理,就会一直输出Your result is pass!

静态文件

hello.html

<html>
   <head>
      <script type = "text/javascript" 
         src = "{{  url_for('static', filename = 'hello.js') }}" ></script>
   </head>
   <body>
      <input type = "button" onclick = "sayHello()" value = "Say Hello" />
   </body>
</html>

hello.js

function sayHello() { 
    alert("Hello World")
 }
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
   return render_template("hello.html")

if __name__ == "__main__":
    app.run('0.0.0.0',debug = True)

访问http://127.0.0.1:5000/点击Say Hello弹窗Hello World

Cookies

from flask import Flask,redirect,url_for,request,render_template,make_response,escape,session
@app.route('/')
def index():
   return render_template('index.html')
@app.route('/setcookie',methods = ['POST','GET'])
def setcookie():
   if request.method == 'POST':
       user = request.form['nm']
       resp = make_response(render_template('readcookie.html'))
       resp.set_cookie('userID',user)
       return resp
@app.route('/getcookie')
def getcookie():
   name = request.cookies.get('userID')
   a = "<h1>welcome '{ }'</h1>".format(name)
   return a
   
if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

index.html

<html>
<body>
    <form action="http://192.168.0.102:5000/setcookie" method="POST">
        <p><h3>Enter userID</h3></p>
        <p><input type='text' name='nm' /></p>
        <p><input type='submit' value='Login' /></p>
    </form>
</body>
</html>

readcookie.html

<html>
<body>
    <a  href="http://192.168.0.102:5000/getcookie">Click here to read cookie</a>
</body>
</html>

首先在访问http://127.0.0.1:5000/传入flask,点击Click here to read cookie即可看到welcome 'flask'

会话

session是存储在服务器上的,会话是客户端登录到服务器并注销服务器的时间间隔。需要在该会话中保存的数据会存储在服务器上的临时目录中。

# -*- coding:UTF-8 -*-
from flask import Flask,redirect,url_for,request,render_template,make_response,escape,session
import os
app = Flask(__name__)

app.secret_key = 'any random string'
#app.secret_key = os.urandom(12)
@app.route('/')
def index():
    if 'username' in session:
        username = session['username']
        return 'Logged in as ' + username + '<br>' + "<b><a href = '/logout'>click here to log out</a></b>"
    return "You are not logged in <br><a href = '/login'></b>" + "click here to log in</b></a>"
@app.route('/login', methods = ['POST','GET'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
   <form action = "" method = "post">
      <p><input type = text name = username /></p>
      <p><input type = submit value = Login /></p>
   </form>
   '''
@app.route('/logout')
def logout():
    session.pop('username',None)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

这里将session_key设置成了any random string

重定向和错误

# -*- coding:UTF-8 -*-
from flask import Flask,redirect,url_for,request,render_template,make_response,escape,session
import os
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('login.html')

@app.route('/login',methods = ['POST','GET'])
def login():
    if request.method == 'POST' and request.form['username'] == 'admin':
        return redirect(url_for('success'))
    return redirect(url_for('index'))

@app.route('/success')
def success():
    return 'logged in successfully'

if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

当用户输入不为admin时则直接302重定向到login界面

login.html

<html>
   <body>
      <form action = "http://192.168.0.102:5000/login" method = "post">
         <p>Enter Name:</p>
         <p><input type = "text" name = "username" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
   </body>
</html>

报错:

# -*- coding:UTF-8 -*-
from flask import Flask,redirect,url_for,request,render_template,make_response,escape,session,abort
import os
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('login.html')

@app.route('/login',methods = ['POST','GET'])
def login():
    if request.method == 'POST':
        if request.form['username'] == 'admin':
            return redirect(url_for('success'))
        else:
            abort(401)
    else:
        return redirect(url_for('index'))

@app.route('/success')
def success():
    return 'logged in successfully'

if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

Flask.abort(code):带有错误代码的abort函数

  • 400 - 用于错误请求
  • 401 - 用于未身份验证的
  • 403 - Forbidden
  • 404 - 未不到
  • 406 - 表示不接受
  • 415 - 用于不支持的媒体类型
  • 429 - 请求过多

若登录不是admin则报401:Unauthorized错误

消息闪现

# -*- coding:UTF-8 -*-
from flask import Flask,redirect,url_for,request,render_template,make_response,escape,session,abort,flash
import os
app = Flask(__name__)
app.secret_key = os.urandom(12)
@app.route('/')
def index():
    return render_template('index.html')

@app.route('/login',methods = ['POST','GET'])
def login():
    error = None
    if request.method == 'POST':
        if request.form['username'] != 'admin' or request.form['password'] != 'admin':
            error = 'Invalid username or password. Please try again!'
        else:
            flash('You were successfully logged in')
            return redirect(url_for('index'))
    
    return render_template('login.html', error = error)

if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

flash(message, category):将消息传递给下一个请求,该请求通常是一个模板。

  • message:参数是要闪现的实际消息。
  • category:参数是可选的。它可以是“error”,“info”或“warning”。

get_flashed_messages(with_categories, category_filter):从会话中删除消息

两个参数都是可选的。如果接收到的消息具有类别,则第一个参数是元组。第二个参数仅用于显示特定消息。第一个默认为False

典型的消息闪现模板:

{% with messages = get_flashed_messages() %}
   {% if messages %}
      {% for message in messages %}
         {{  message }}
      {% endfor %}
   {% endif %}
{% endwith %}

下面给出html代码:

index.html

<html>
{% with messages = get_flashed_messages() %}
    {% if messages %}
        <ul>
          {% for message in messages %}
          <li>{ {  message }}</li>
          {% endfor %}
        </ul>
    {% endif %}
 {% endwith %}
 <h1>Flask Message Flashing Example</h1>
 <p>Do you want to <a href = "{{  url_for('login') }}"><b>log in?</b></a></p>
</html>

login.html

<html>
   <!doctype html>
   <h1>Login</h1>
   {% if error %}
   <p><strong>Error:</strong> {{  error }}
   {% endif %}
   <form action = "" method = post>
      <dl>
         <dt>Username:</dt> 
      <dd>
         <input type = text name = username value = "{{ request.form.username }}">
      </dd>
 
      <dt>Password:</dt>
      <dd><input type = password name = password></dd>
      </dl>
  <p><input type = submit value = Login></p>
   </form>
</html>

访问http://127.0.0.1:5000/点击login后进入登录界面,随后如果输入的username和password不是admin的话会在界面上显示error消息,若登录成功则跳转到index页面上方显示登录成功消息

文件上传

upload.html

<!doctype html>
<title>Upload new File</title>
{% with messages = get_flashed_messages() %}
    {% if messages %}
        <ul>
          {% for message in messages %}
          <p>{{  message }}</p>
          {% endfor %}
        </ul>
    {% endif %}
 {% endwith %}
<h1>Upload new File</h1>
<form method=post enctype=multipart/form-data>
  <input type=file name=file>
  <input type=submit value=Upload>
</form>
import os
from flask import Flask, flash, request, redirect, url_for,send_from_directory,render_template
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = r'E:\\flask\\upload\\'
ALLOWED_EXTENSIONS = { 'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.secret_key = os.urandom(12)
def allowed_file(filename):  #判断上传的文件是否非法
    if filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS:
        return filename
    else:
        flash('illegal file')
        return False

@app.route('/')
def upload():
    return render_template('upload.html')

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files: 
            flash('No file part')
            return redirect(url_for('upload'))
        file = request.files['file']  
        # if user does not select file, browser also
        # submit an empty part without filename
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):  
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))#save方法保存文件
            return redirect(url_for('uploaded_file',filename=filename))
    return redirect(url_for('upload'))


@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],filename)

if __name__ == '__main__':
    app.run('0.0.0.0',debug = True)

不得不说,官方的代码还是要强一些,当上传为空时会报错,非法文件也会报错,但上传成功后跳转到上传的文件url处

UPLOAD_FOLDER 是上传文 件要储存的目录, ALLOWED_EXTENSIONS 是允许上传的文件扩展名的集合,MAX_CONTENT_LENGTH是限制的上传文件的大小

扩展

  • Flask Mail - 为Flask应用程序提供SMTP接口
  • Flask WTF - 添加WTForms的渲染和验证
  • Flask SQLAlchemy - 为Flask应用程序添加SQLAlchemy支持
  • Flask Sijax - Sijax的接口 - Python/jQuery库,使AJAX易于在Web应用程序中使用

参考链接:

https://dormousehole.readthedocs.io/en/latest/index.html (官方文档)

https://www.w3cschool.cn/flask/ (W3Cschool教程)