概述
Flask是一个用Python编写的Web应用程序框架。 它由 Armin Ronacher 开发,他领导一个名为Pocco的国际Python爱好者团队。 Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。两者都是Pocco项目。
环境
Python2.7及以上,Python3.4及以上,我这里用的Python2.7
安装virtualenv
virtualenv可以创建一个独立的python运行环境,这个环境和系统的python环境是互不干扰的,也就是说你在这个环境中安装的python包不会安装到系统的python环境中,系统python环境中的包会复制到这个环境中(但新版本的virtualenv并不会复制过来,默认只安装setuptools
,wheel
和pip
)
当然如果不想用系统的包的话可以加一些参数:
–-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教程)
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues