# flask

Flask是最轻量的python web框架，仅仅看完了Flask官方文档中的Quickstart部分，就可以实现简单的网站了。当然，网站看起来好不好，与html, CSS和js水平有关，这不在Flask的使用范围之内。

[flask官方教程](https://dormousehole.readthedocs.io/en/latest/)

flask常用命令：

* 安装 `pip install Flask`
* 运行`flask`程序前设置变量`export FLASK_APP=flaskr`
* 调试模式`export FLASK_ENV=development`
  * 如果你打开调试模式，那么服务器会在修改应用代码之后自动重启， 并且当应用出错时还会提供一个有用的调试器。
* 运行`flask`程序`flask run`或者`python -m flask run`
* 设置外部可见`flask run --host=0.0.0.0`

文件结构

```python
/home/user/Projects/flask-tutorial # 项目主目录
├── flaskr/ 
│ ├── __init__.py 
│ ├── db.py 
│ ├── schema.sql 
│ ├── auth.py 
│ ├── blog.py 
│ ├── templates/ 
│ │ ├── base.html 
│ │ ├── auth/ 
│ │ │ ├── login.html 
│ │ │ └── register.html 
│ │ └── blog/ 
│ │ ├── create.html 
│ │ ├── index.html 
│ │ └── update.html 
│ └── static/ 
│ └── style.css 
├── tests/ 
│ ├── conftest.py 
│ ├── data.sql 
│ ├── test_factory.py 
│ ├── test_db.py 
│ ├── test_auth.py 
│ └── test_blog.py 
├── venv/ 
├── setup.py 
└── MANIFEST.in
```

## flask应用工厂

```python
# /flaskr/__init__.py
import os
from flask import Flask
def create_app(test_config=None):
    # create and configure the app
    app = Flask(__name__, instance_relative_config=True) 
    app.config.from_mapping(
        SECRET_KEY='dev',
        DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'), 
    )
    if test_config is None:
    # load the instance config, if it exists, when not testing
        app.config.from_pyfile('config.py', silent=True)
    else:
        # load the test config if passed in
        app.config.from_mapping(test_config)
    # ensure the instance folder exists
    try:
        os.makedirs(app.instance_path) 
    except OSError:
        pass
    # a simple page that says hello
    @app.route('/hello') 
    def hello():
        return 'Hello, World!'
    return app
```

1. `app = Flask(__name__, instance_relative_config=True)`创建Flask 实例。
   1. `__name__` 是当前 Python 模块的名称。应用需要知道在哪里设置路径，使用 `__name__` 是一个方 便的方法。
   2. instance\_relative\_config=True 告诉应用配置文件是相对于instance folder 的相对路径。实例文件夹在 flaskr 包的外面，用于存放本地数据（例如配置密钥和数据库），不应当提交到版本 控制系统。
2. `app.config.from_mapping()` 设置一个应用的缺省配置：
   1. SECRET\_KEY 是被 Flask 和扩展用于保证数据安全的。在开发过程中，为了方便可以设置为 'dev' ，但是在发布的时候应当使用一个随机值来重载它。
   2. DATABASE SQLite 数据库文件存放在路径。它位于 Flask 用于存放实例的app.instance\_path之内。下一节会更详细地学习数据库的东西。
3. `app.config.from_pyfile()` 使用 `config.py` 中的值来重载缺省配置，如果 config.py 存在的 话。例如，当正式部署的时候，用于设置一个正式的 SECRET\_KEY 。
4. `os.makedirs()` 可以确保app.instance\_path 存在。Flask 不会自动创建实例文件夹，但是必须确保创建这个文件夹，因为 SQLite 数据库文件会被保存在里面。
5. `@app.route()` 创建一个简单的路由，这样在继续教程下面的内容前你可以先看看应用如何运行的。 它创建了 URL /hello 和一个函数之间的关联。这个函数会返回一个响应，即一个 'Hello, World!'

## 蓝图

略

## 视图

略

## WSGI应用

全称是Web Server Gateway Interface，其主要作用是Web服务器与Python Web应用程序或框架之间的建议标准接口，以促进跨各种Web服务器的Web应用程序可移植性。

WSGI并不是框架而只是一种协议，我们可以将WSGI协议分成三个组件Application，Server，Middleware和协议中传输的内容。

## 最小的flask应用

```python
from flask import Flask 
app = Flask(__name__)

@app.route('/') 
def hello_world():
    return 'Hello, World!'
```

## route装饰器

使用route() 装饰器来把函数绑定到 URL.

## 通过url传递变量

`route`装饰器中还可以设置变量。通过把 URL 的一部分标记为 `<variable_name>` 就可以在 URL 中添加变量。标记的部分会作为关键字参数传递给函数。通过使用 <converter:variable_name> ，可以选择性的加上一个转换器，为变量指定规则。

```python
@app.route('/post/<int:post_id>') 
def show_post(post_id):
    # show the post with the given id, the id is an integer 
    return 'Post %d' % post_id
```

变量的类型有如下五种：

| 类型     | 说明                 |
| ------ | ------------------ |
| string | （缺省值）接受任何不包含斜杠的文本  |
| int    | 接受正整数              |
| float  | 接受正浮点数             |
| path   | 类似 string ，但可以包含斜杠 |
| uuid   | 接受 UUID 字符串        |

## url重定向

```python
@app.route('/projects/') 
def projects():
    return 'The project page'

@app.route('/about') 
def about():
    return 'The about page'
```

projects 的 URL 是中规中矩的，尾部有一个斜杠，看起来就如同一个文件夹。访问一个没有斜杠结尾的 URL 时 Flask 会自动进行重定向，帮你在尾部加上一个斜杠。

about 的 URL 没有尾部斜杠，因此其行为表现与一个文件类似。如果访问这个 URL 时添加了尾部斜杠就会 得到一个 404 错误。这样可以保持 URL 唯一，并帮助搜索引擎避免重复索引同一页面。

## http方法

http方法公有9种，其中常用的如下：

| 方法     | 描述                                                                      |
| ------ | ----------------------------------------------------------------------- |
| GET    | 请求指定的页面信息，并返回实体主体。                                                      |
| HEAD   | 类似于 GET 请求，只不过返回的响应中没有具体的内容，用于获取报头                                      |
| POST   | 向指定资源提交数据进行处理请求（例如提交表单或者上传文件）。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
| PUT    | 从客户端向服务器传送的数据取代指定的文档的内容。                                                |
| DELETE | 请求服务器删除指定的页面。                                                           |

缺省情况下，一个路 由只回应 GET 请求。可以使用route() 装饰器的 methods 参数来处理不同的 HTTP 方法。

`@app.route('/login', methods=['GET', 'POST'])`

## 渲染模板

在python内部生成的html不好阅读，且功能有限，必须自己负责html转义。Falsk自动配置了`Jinja2`模板引擎。

使用render\_template() 方法可以渲染模板，你只要提供模板名称和需要作为参数传递给模板的变量就 行了。下面是一个简单的模板渲染例子:

```python
from flask import render_template

@app.route('/hello/') 
@app.route('/hello/<name>') 
def hello(name=None):
    return render_template('hello.html', name=name)
```

Flask 会在 templates 文件夹内寻找模板。因此，如果你的应用是一个模块，那么模板文件夹应该在模块旁 边；如果是一个包，那么就应该在包里面：

情形 1 : 一个模块:

```
/application.py 
/templates 
    /hello.html
```

情形 2 : 一个包:

```
/application
    /__init__.py 
    /templates 
        /hello.html
```

可以充分利用Jinja2模板引擎的威力。

## 操作请求数据

对于 web 应用来说对客户端向服务器发送的数据作出响应很重要。在 Flask 中由全局对象request 来提供请求信息。那么，这个全局变量怎么保证线程安全呢？

实际上，这些对象不是通常意义下的全局对象。这些对象实际上是特定环境下本地对象的代理。

## 接收请求对象

导入`request`对象，然后获取数据。

```python
from flask import request

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None 
    if request.method == 'POST':
        if valid_login(request.form['username'], 
                        request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password' 
    # the code below is executed if the request method 
    # was GET or the credentials were invalid 
    return render_template('login.html', error=error)
```

当 form 属性中不存在这个键时会发生什么？会引发一个 KeyError.

要操作 URL （如 ?key=value ）中提交的参数可以使用args 属性:

`searchword = request.args.get('key', '')`

## 文件上传

略

## cookies

略

## 会话session

会话相对Cookies更加安全。

`session`对象，允许你在不同请求之间储存信息。这个对象相当于用密钥签名加密的 cookie ，即用户可以查看你的 cookie ，但是如果没有密钥就无法修改它。

使用会话之前你必须设置一个密钥。举例说明:

```python
from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username']) 
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username'] return redirect(url_for('index')) 
    return '''
        <form method="post">
            <p><input type=text name=username> 
            <p><input type=submit value=Login> 
        </form>
        '''
@app.route('/logout') 
def logout():
    # remove the username from the session if it's there
    session.pop('username', None) 
    return redirect(url_for('index'))
```

## 重定向和错误

略

## 日志

略

## 集成WSGI中间件

略

## test\_request\_context() 环境管理器

```python
from flask import request

with app.test_request_context('/hello', method='POST'):
    # now you can do something with the request until the
    # end of the with block, such as basic assertions: 
    assert request.path == '/hello' 
    assert request.method == 'POST'
```

略

## 示例

### 一个展示图片的网站

受到`http://thispersondoesnotexist.com`的启发，用开源库`https://github.com/a312863063/seeprettyface-generator-yellow`生成虚拟图片，然后加上以下代码做一个网页。

```python
from flask import Flask, make_response
import random, glob

app = Flask(__name__,instance_relative_config=True)
# app配置
app.config.from_mapping(
    SECRET_KEY='dev'
)

@app.route('/')
def index():
    img_path = random.choice(glob.glob('result/*.png'))
    print(img_path)
    image_data = open(img_path, "rb").read()
    response = make_response(image_data)
    response.headers['Content-Type'] = 'image/png'
    return response

if __name__=="__main__":
    app.run(host='0.0.0.0',port=5010,debug=False)
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://niyunsheng.gitbook.io/deeplearning/wang-zhan/flask.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
