Flask与Django区别
Flask - 微框架、灵活、扩展性强、按需组合
Django - 大而全、开箱即用、方便、灵活稍差
Flask主要包含
* Web服务器网关接口 Werkzeug
(WSGI Web Server Gateway Interface)
* 模板系统 Jinja2
其他包可自行扩展
安装和运行 (虚拟环境)
mkdir src
python3 -m venv venv
source venv/bin/activate
pip install flask
简单的例子 hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>你好未来</h1>'
@app.route('/user/<name>')
def user(name):
return '<h2>你好啊, %s</h2>' % name
if __name__ == '__main__':
app.run(debug=True)
# app.run(debug=True, host='0.0.0.0', port=5001)
程序上下文和请求上下文 context
Flask使用上下文让特定的变量只在一个线程中全局可访问,与此同时不会干扰其他其他线程。为了视图访问方便,Flask使用上下文临时将某些对象变为在一个请求线程中全局可访问
变量名 | 上下文 | 说明 |
current_app | 程序上下文 | 当前激活程序的实例 |
g | 程序上下文 | 处理请求时用作临时存储的对象,每次的请求都会重设这个变量 |
request | 请求上下文 | 请求对象,封装了客户端发出的HTTP请求中的内容 |
session | 请求上下文 | 用户会话,用于存储请求之间需要**”记住“**的值的词典 |
from flask import request
@app.route('/getip')
def getip():
ip = request.remote_addr
user_agent = request.headers.get('User-Agent')
return '<h1>你的ip地址是%s, User-Agent是%s</h2>' % (ip, user_agent)
在这个视图函数中我们如何把request当作全局变量使用。事实上request不可能是全局变量,试想在多线程的服务器中,多个线程同时处理不同客户端发送的请求时,每个线程看到的request必然是不同的,此时上下文起到了至关重要的作用。
>>> from hello import app
>>> from flask import current_app
>>> current_app.name
Traceback (most recent call last):
...
RuntimeError: working outside of application context
>>>app_ctx = app.app_context()
>>>current_app.name
'hello'
>>>app_ctx.pop()
在这个例子中。没有激活上下文之前就调用current_app.name就会发生错误,但激活程序完再调用就不会出错了。<app名字>.app_context()获得的是当前程序的上下文。
响应
Flask默认的响应是200,如果需要视图函数返回不同的状态码,可以把数字代码作为返回值,添加到响应文本。下面的视图函数返回400状态码,表示请求无效:
from flask import make_response
# 自动构建响应, 可以设置返回的状态
@app.route('/diffrequest')
def diffrequest():
return '<h2>Bad request</h2>', 400
Flask视图函数还可以返回Response对象。make_response()函数可以接收以1个、2个或3个参数(和视图函数的返回值一样),并返回一个Renponse对象。下面创建了一个响应对象,然后设置了cookie:
from flask import make_response
@app.route('/login/<username>')
def login(username):
response = make_response('<h1>手动构建一个响应,存储cookie</h1>')
response.set_cookie('username', username)
return response
重定向
没有返回页面,但是给浏览器一个新地址进行加载,状态码302
# 重定向
@app.route('/jump')
def jump():
username = 'jon'
return redirect('/login/%s' % username)
abort抛异常的方法
# abort抛异常的方法
@app.route('/guess/<int:number>')
def guess(number):
if number < 8000:
abort(404)
return '<h1>你猜对了</h1>'
Flask-Script
方便启动flask程序的时候传递启动参数
$ pip install flask-script
from flask_script import Manager
manager = Manager(app)
if __name__ == '__main__':
manager.run()
$ python hello.py runserver --help
允许其他机器访问服务器
$ python hello.py runserver -h 0.0.0.0 -p 5001
jinja2模板
Flask 默认在程序相同文件夹中的templates子文件夹中找模板
from flask import Flask, render_template
from flask_script import Manager
app = Flask(__name__)
manager = Manager(app)
# 渲染一个简单的模板, 模板默认在templates中
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
manager.run()
使用模板变量
@app.route('/usevar/<name>')
def usevar(name):
result_list = ['苹果', '梨', '西瓜']
result_dict = { '姓名': '吴东', '生日': '1999-10-21' }
return render_template('usevar.html', name=name, result_list=result_list, result_dict=result_dict)
Jinja2便令过滤器语法:{{ 变量|过滤器|过滤器… }}
过滤器名 | 说明 |
capitalize | 值的首字母大写,其他字母小写 |
lower | 值转换为小写 |
upper | 值转换为大写 |
title | 值的每个单词的首字母大写 |
trim | 值的收尾空格去掉 |
striptages | 渲染之前把所有的HTML标签都删掉 |
safe | 渲染值时不转义 |
reverse | 单词反转 |
例如:首字母大写形式显示name:Hello, {{ name|capitalize }}
safe 过滤器值特别说明一下:认情况下,出于安全考虑,Jinja2会转义所有的变量,例如如果一个变量的值为tag='<h1>Hello</h1>'
,Jinja2会将其在页面渲染成<h1>hello</h1>
很多时候需要显示变量中存储的HTML代码,这时就可以用safe过滤器{{ tag|safe}}
模板控制结构
如何再模板中使用条件语句和循环语句
{% if result_list %}
<ul>
{% for item in result_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% else %}
<h2>篮子里是空的</h2>
{% endif %}
宏,在模板上定义的函数,类似python中定义的函数
{% macro f(content) %}
<span style="color:blue;">{{ content }}</span>
{% endmacro %}
<div>{{ f('蓝色的') }}</div>
模板继承
base.html
<!DOCTYPE html>
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %}</title>
{% endblock %}
</head>
<body>
{% block body %}
<div class="a1">模板上的默认文字</div>
{% endblock %}
</body>
</html>
child.html
{% extends "base.html" %}
{% block title %}母版页测试{% endblock %}
{% block head %}
{{ super() }}
<style>
.a1 { color:red; }
</style>
{% endblock %}
{% block body %}
{{ super() }}
<div>今天天气如何?</div>
{% endblock %}
自定义错误页面
# 自定义错误页面
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def errorhandler(e):
return render_template('500.html'), 500
链接
url_for 用视图函数名返回对应的url
# 相对链接 'index'是视图函数的名字
url = url_for('index')
# 'users'生成蓝图对象时的第一个参数,'index'视图函数名
users = Blueprint('user', __name__)
url = url_for('user.index')
# 绝对链接,比如放在邮件里面会生成这样的地址: http://127.0.0.1:5000/
url = url_for('index', _external=True)
# 传递参数
url = url_for('login', username='carmack', v=1) # /login/carmack/?v=1
静态文件
默认放在static目录下,包括图片,css, javascript
<img src="{{ url_for('static', filename='images/img_2569.jpg') }}" width="400px;">
视图里如何读取客户端传入的数据
# 读取GET方式传入的数据
tag = request.args.get('tag')
# 读取POST方式传入的数据
username = request.form.get('username')
CSRF (Cross-Site Request Forgery) 恶意网站把请求发送到被攻击者已登录的其他网站,防止方式:设置一个密钥,Flask用这个密钥生成加密的令牌,再用令牌验证表单数据
服务器:app.config['SECRET_KEY'] = 'q234asdfad@#$AdfS*UNFs'
客户端:在form表单中添加{{ form.csrf_token }}
使用Werkzeug实现密码散列
from werkzeug.security import generate_password_hash, check_password_hash
generate_password_hash(password,method=pbkdf2:sha1,salt_length=8)
这个函数将原始密码作为输入,以字符串形式输出密码的散列值,输出的值可保存在用户数据库中。
method 和salt_length 的默认值就能满足大多数需求。
check_password_hash(hash, password)
这个函数的参数是从数据库中取回的密码散列 值和用户输入的密码。返回值为True表明密码正确