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会将其在页面渲染成&lt;h1&gt;hello&lt;/h1&gt;很多时候需要显示变量中存储的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表明密码正确