标签归档:flask

Flask ImportError:没有名为Flask的模块

问题:Flask ImportError:没有名为Flask的模块

我在这里关注Flask教程:

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

我到了尝试./run.py的地步,我得到了:

Traceback (most recent call last):
  File "./run.py", line 3, in <module>
    from app import app
  File "/Users/benjaminclayman/Desktop/microblog/app/__init__.py", line 1, in <module>
    from flask import Flask
ImportError: No module named flask

看起来类似于:

ImportError:没有名为flask的模块

但是他们的解决方案没有帮助。作为参考,我确实有一个名为flask的文件夹,一个用户提到该文件夹​​可能会引起问题。

I’m following the Flask tutorial here:

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

I get to the point where I try ./run.py and I get:

Traceback (most recent call last):
  File "./run.py", line 3, in <module>
    from app import app
  File "/Users/benjaminclayman/Desktop/microblog/app/__init__.py", line 1, in <module>
    from flask import Flask
ImportError: No module named flask

This looks similar to:

ImportError: No module named flask

But their solutions aren’t helpful. For reference, I do have a folder named flask which one user mentioned may cause issues.


回答 0

尝试删除您创建的virtualenv。然后使用以下命令创建一个新的virtualenv:

virtualenv flask

然后:

cd flask

现在让我们激活virtualenv:

source bin/activate

现在,您应该(flask)在命令行左侧看到。

让我们安装烧瓶:

pip install flask

然后创建一个名为hello.py(注意:见UPDATE Flask 1.0.2下文)的文件:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

并运行:

python hello.py

更新Flask 1.0.2

使用新的flask版本,无需从脚本中运行该应用程序。 hello.py现在应该看起来像这样:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

并运行:

FLASK_APP=hello.py flask run

hello.py运行最新命令时,请确保位于其中的文件夹中。

创建hello.py之前的所有步骤同样适用于这种情况

Try deleting the virtualenv you created. Then create a new virtualenv with:

virtualenv flask

Then:

cd flask

Now let’s activate the virtualenv

source bin/activate

Now you should see (flask) on the left of the command line.

Edit: In windows there is no “source” that’s a linux thing, instead execute the activate.bat file, here I do it using Powershell: PS C:\DEV\aProject> & .\Flask\Scripts\activate)

Let’s install flask:

pip install flask

Then create a file named hello.py (NOTE: see UPDATE Flask 1.0.2 below):

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

and run it with:

python hello.py

UPDATE Flask 1.0.2

With the new flask release there is no need to run the app from your script. hello.py should look like this now:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

and run it with:

FLASK_APP=hello.py flask run

Make sure to be inside the folder where hello.py is when running the latest command.

All the steps before the creation of the hello.py apply for this case as well


回答 1

对于python 3使用

pip3安装烧瓶

For python 3 use

pip3 install flask


回答 2

我唯一可以解决的方法是将用户python目录添加到myapp.wsgi文件中。举个例子:

sys.path.append('/home/deployer/anaconda3/lib/python3.5/site-packages')

我猜想,如果将软件包安装在全局环境中,应该没有问题,但是我已经以用户身份安装了python软件包。

The only way I could solve was by adding my users python dir to myapp.wsgi file. As an example:

sys.path.append('/home/deployer/anaconda3/lib/python3.5/site-packages')

I guess that if you install the packages in the global enviroment, you should have no problem, but I had my python packages installed as user.


回答 3

激活虚拟环境并安装Flask之后,我创建了一个app.py文件。我这样运行:python -m flask run。希望这会有所帮助!

After activating the virtual environment and installing Flask, I created an app.py file. I run it like this : python -m flask run. Hope this will help!


回答 4

我对flasgger也有类似的问题。

原因是我经常使用

sudo pip install flask

但是由于某些原因,这并不总是可行的。有时候,你必须要做的只是

pip install flask

另一个陷阱是有时人们会pip install Flask大写字母F键入

如果有人卡住,请将其张贴在这里。让我知道是否有帮助。

有用的链接: pip install和sudo pip install有什么区别?

I had a similar problem with flasgger.

The reason for that was that I always use

sudo pip install flask

but for some reason that’s not always the way to go. Sometimes, you have to do just

pip install flask

Another gotcha is that sometimes people type pip install Flask with the cap F

Posting this here in case somebody gets stuck. Let me know if it helped.

Useful Link: What is the difference between pip install and sudo pip install?


回答 5

这对我有用

sudo -H pip install flask

或者对于pip3(python3)使用:

sudo -H pip3 install flask

边注

如果您使用的是virtualenv,则最好 pip freeze >> requirements.txt 将已安装的软件包放在一个位置。该sudo命令和-H标志。欲了解更多有关sudo-H标志,看看保罗的 回答。希望对您有帮助。

this is what worked for me,

sudo -H pip install flask

Or for pip3(python3) use :

sudo -H pip3 install flask

Sidenote

If you’re using virtualenv it’s a good idea to pip freeze >> requirements.txt to allow for the installed packages to be listed in one place. The sudo command and -H flag. For more on sudo‘s -H flag, look at Paul’s answer. Hope this helps you.


回答 6

我正在使用python2,但安装了它:sudo apt-get install libapache2-mod-wsgi-py3

代替:sudo apt-get install libapache2-mod-wsgi

纠正安装解决了no flask问题。

I was using python2 but installed this: sudo apt-get install libapache2-mod-wsgi-py3

Instead of: sudo apt-get install libapache2-mod-wsgi

Correcting the installation solved the no flask problem.


回答 7

就我而言,解决方案就像启动虚拟环境一样简单:

$ venv/scripts/activate

事实证明,我对Python还是很新鲜的:)

In my case the solution was as simple as starting up my virtual environment like so:

$ venv/scripts/activate

It turns out I am still fresh to Python :)


回答 8

  1. 编辑 /etc/apache2/sites-available/FlaskApp.conf
  2. 在“ WSGIScriptAlias”行之前添加以下两行:

WSGIDaemonProcess FlaskApp python-home=/var/www/FlaskApp/FlaskApp/venv/FlaskApp WSGIProcessGroup FlaskApp

  1. 重新启动Apache:service apache2 restart

我也遵循了Flask教程,并且遇到了同样的问题,我找到了解决问题的方法。

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

  1. Edit /etc/apache2/sites-available/FlaskApp.conf
  2. Add the following two lines before the “WSGIScriptAlias” line:

WSGIDaemonProcess FlaskApp python-home=/var/www/FlaskApp/FlaskApp/venv/FlaskApp WSGIProcessGroup FlaskApp

  1. Restart Apache:service apache2 restart

I’m following the Flask tutorial too.And I met the same problem.I found this way to fix it.

http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world


回答 9

转到微博客中的flask文件,然后使用激活虚拟环境source bin/activate,然后转到flask / bin并安装flask,以及其余的软件包pip install flask。您会在bin目录中看到烧瓶。尝试./run.py从微博客(或任何有文件的地方)再次运行。

Go to the flask file in microblog, then activate the virtual environment with source bin/activate, then go to flask/bin and install flask, and the rest of the packages, pip install flask. You will see flask listed inside bin directory. Try to run ./run.py again from microblog (or from wherever you have the file).


回答 10

即使我也建议您使用virtualenv,这也可以解决您的问题。

sudo apt install python-flask

如果您想在生产服务器中进行部署,则继续上述解决方案,否则请使用virtualenv。

Even i too suggest u virtualenv, This might also solve ur problem.

sudo apt install python-flask

If u want to deploy in productionserver then go ahead with above solution else use virtualenv.


回答 11

当我在Windows中遇到类似错误时,这对我有用。1.安装virtualenv

pip install virtualenve
  1. 创建一个virtualenv

    虚拟环境瓶

  2. 导航到脚本并激活virtualenv

    启用

  3. 安装烧瓶

    python -m pip安装烧瓶

  4. 检查烧瓶是否已安装

    python -m pip列表

This is what worked for me when I got a similar error in Windows; 1. Install virtualenv

pip install virtualenve
  1. Create a virtualenv

    virtualenv flask

  2. Navigate to Scripts and activate the virtualenv

    activate

  3. Install Flask

    python -m pip install flask

  4. Check if flask is installed

    python -m pip list


回答 12

输入您的python交互模式,然后:

import sys

sys.path

它将打印您的路径。检查sys.path中是否安装了保温瓶。

对于MacOS,python路径位于/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages下

但是pip’ll默认会在/Library/Python/2.7/site-packages下安装python软件包

这就是为什么它不适用于MacOS。

enter your python interactive mode then:

import sys

sys.path

it will print your path. Check wether flask is installed in the sys.path.

For MacOS, python path is under /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages

But pip’ll install python package by default under /Library/Python/2.7/site-packages

That’s why it doesn’t work for MacOS.


回答 13

flask脚本很适合启动本地开发服务器,但是每次更改代码后,都必须手动重新启动它。那不是很好,Flask可以做得更好。如果启用调试支持,则服务器将在代码更改时重新加载自身,如果出现问题,它还将为您提供有用的调试器。要启用调试模式,您可以在运行服务器之前导出FLASK_DEBUG环境变量:例如您的文件是hello.py

$ export FLASK_APP=hello.py
$ export FLASK_DEBUG=1
$ flask run

The flask script is nice to start a local development server, but you would have to restart it manually after each change to your code. That is not very nice and Flask can do better. If you enable debug support the server will reload itself on code changes, and it will also provide you with a helpful debugger if things go wrong. To enable debug mode you can export the FLASK_DEBUG environment variable before running the server: forexample your file is hello.py

$ export FLASK_APP=hello.py
$ export FLASK_DEBUG=1
$ flask run

回答 14

在我使用Docker的情况下,未复制我的.env文件,因此未设置以下env变量:

.env.local: FLASK_APP=src/app.py

所以在我中Dockerfile我必须包括:

FROM deploy as dev
COPY env ./env

在docker-compose.yml中引用了

env_file: ./env/.env.local

我必须注意的另一件事是path变量,以确保使用我的环境

ENV PATH $CONDA_DIR/envs/:my_environment_name_from_yml_file:/bin:$CONDA_DIR/bin:$PATH```

in my case using Docker, my .env file was not copied, so the following env vars were not set:

.env.local: FLASK_APP=src/app.py

so in my Dockerfile i had to include:

FROM deploy as dev
COPY env ./env

which was referenced in docker-compose.yml

env_file: ./env/.env.local

another thing i had to pay attention to is the path variable to ensure my environment is used

ENV PATH $CONDA_DIR/envs/:my_environment_name_from_yml_file:/bin:$CONDA_DIR/bin:$PATH```

回答 15

我的回答仅适用于使用Visual Studio Flesk Web项目的所有用户:

只需右键单击“ Python环境”,然后单击“添加环境”

my answer just for any users that use Visual Studio Flesk Web project :

Just Right Click on “Python Environment” and Click to “Add Environment”


回答 16

如果您使用的是Pycharm,则这是虚拟环境问题。

因此,在创建Python项目时,您必须选择“现有解释器”选项->单击“系统解释器”->选择正确的选项,例如“ * \ AppData \ Local \ Programs \ Python \ Python3.6 \ python.exe”。

您也可以使用“ New Virtual Env”,但我已经给出了适用于Pycharm用户的快速修复程序。

If you are using Pycharm then this is the virtual environment issue.

So, at the time of creating your Python project you will have to select “Existing interpreter” option -> click “system Interpreter” -> select the correct option for example “*\AppData\Local\Programs\Python\Python3.6\python.exe”.

You can use ‘New Virtual Env’ as well, but I have just given the quick fix that should work for Pycharm users.


回答 17

另一件事-如果你使用python3,请确保您正在使用启动服务器python3 server.py,而不是python server.py

Another thing – if you’re using python3, make sure you are starting your server with python3 server.py, not python server.py


回答 18

升级点数后对我有用:

curl https://bootstrap.pypa.io/get-pip.py | python

在这里找到答案:https : //stackoverflow.com/a/49748494/3197202

然后我可以安装flask:

pip install flask

It worked for me after upgrading pip:

curl https://bootstrap.pypa.io/get-pip.py | python

Found that answer here: https://stackoverflow.com/a/49748494/3197202

Then I could just install flask:

pip install flask

如何在不使用ctrl-c的情况下停止烧瓶应用

问题:如何在不使用ctrl-c的情况下停止烧瓶应用

我想实现一个可以使用flask-script停止flask应用程序的命令。我已经搜索了一段时间。因为该框架不提供“ app.stop()” API,所以我对如何编写此代码感到好奇。我正在使用Ubuntu 12.10和Python 2.7.3。

I want to implement a command which can stop flask application by using flask-script. I have searched the solution for a while. Because the framework doesn’t provide “app.stop()” API, I am curious about how to code this. I am working on Ubuntu 12.10 and Python 2.7.3.


回答 0

如果只是在桌面上运行服务器,则可以公开终结点以终止服务器(有关更多信息,请参见Shutdown The Simple Server):

from flask import request
def shutdown_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()

@app.route('/shutdown', methods=['POST'])
def shutdown():
    shutdown_server()
    return 'Server shutting down...'

这是另一种包含更多的方法:

from multiprocessing import Process

server = Process(target=app.run)
server.start()
# ...
server.terminate()
server.join()

让我知道是否有帮助。

If you are just running the server on your desktop, you can expose an endpoint to kill the server (read more at Shutdown The Simple Server):

from flask import request
def shutdown_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()

@app.route('/shutdown', methods=['POST'])
def shutdown():
    shutdown_server()
    return 'Server shutting down...'

Here is another approach that is more contained:

from multiprocessing import Process

server = Process(target=app.run)
server.start()
# ...
server.terminate()
server.join()

Let me know if this helps.


回答 1

我使用线程做了一点不同

from werkzeug.serving import make_server

class ServerThread(threading.Thread):

    def __init__(self, app):
        threading.Thread.__init__(self)
        self.srv = make_server('127.0.0.1', 5000, app)
        self.ctx = app.app_context()
        self.ctx.push()

    def run(self):
        log.info('starting server')
        self.srv.serve_forever()

    def shutdown(self):
        self.srv.shutdown()

def start_server():
    global server
    app = flask.Flask('myapp')
    ...
    server = ServerThread(app)
    server.start()
    log.info('server started')

def stop_server():
    global server
    server.shutdown()

我用它来进行静态API的端到端测试,在这里我可以使用python请求库发送请求。

I did it slightly different using threads

from werkzeug.serving import make_server

class ServerThread(threading.Thread):

    def __init__(self, app):
        threading.Thread.__init__(self)
        self.srv = make_server('127.0.0.1', 5000, app)
        self.ctx = app.app_context()
        self.ctx.push()

    def run(self):
        log.info('starting server')
        self.srv.serve_forever()

    def shutdown(self):
        self.srv.shutdown()

def start_server():
    global server
    app = flask.Flask('myapp')
    ...
    server = ServerThread(app)
    server.start()
    log.info('server started')

def stop_server():
    global server
    server.shutdown()

I use it to do end to end tests for restful api, where I can send requests using the python requests library.


回答 2

这是一个有点旧的线程,但是如果有人从后台运行的脚本开始尝试,学习或测试基本的flask应用,则停止它的最快方法是终止正在运行您的应用的端口上运行的进程上。注意:我知道作者正在寻找一种不杀死或停止该应用程序的方式。但这可能会对正在学习的人有所帮助。

sudo netstat -tulnp | grep :5001

你会得到这样的东西。

tcp 0 0 0.0.0.0:5001 0.0.0.0:* LISTEN 28834 / python

要停止应用,请终止进程

sudo kill 28834

This is a bit old thread, but if someone experimenting, learning, or testing basic flask app, started from a script that runs in the background, the quickest way to stop it is to kill the process running on the port you are running your app on. Note: I am aware the author is looking for a way not to kill or stop the app. But this may help someone who is learning.

sudo netstat -tulnp | grep :5001

You’ll get something like this.

tcp 0 0 0.0.0.0:5001 0.0.0.0:* LISTEN 28834/python

To stop the app, kill the process

sudo kill 28834

回答 3

我的方法可以通过bash终端/控制台进行

1)运行并获取进程号

$ ps aux | grep yourAppKeywords

2a)终止进程

$ kill processNum

2b)如果以上方法无效,则终止该进程

$ kill -9 processNum

My method can be proceeded via bash terminal/console

1) run and get the process number

$ ps aux | grep yourAppKeywords

2a) kill the process

$ kill processNum

2b) kill the process if above not working

$ kill -9 processNum

回答 4

正如其他人指出的那样,您只能werkzeug.server.shutdown在请求处理程序中使用。我发现在另一时间关闭服务器的唯一方法是向自己发送请求。例如,/kill此片段中的处理程序将杀死开发服务器,除非在下一秒内收到另一个请求:

import requests
from threading import Timer
from flask import request
import time

LAST_REQUEST_MS = 0
@app.before_request
def update_last_request_ms():
    global LAST_REQUEST_MS
    LAST_REQUEST_MS = time.time() * 1000


@app.route('/seriouslykill', methods=['POST'])
def seriouslykill():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    return "Shutting down..."


@app.route('/kill', methods=['POST'])
def kill():
    last_ms = LAST_REQUEST_MS
    def shutdown():
        if LAST_REQUEST_MS <= last_ms:  # subsequent requests abort shutdown
            requests.post('http://localhost:5000/seriouslykill')
        else:
            pass

    Timer(1.0, shutdown).start()  # wait 1 second
    return "Shutting down..."

As others have pointed out, you can only use werkzeug.server.shutdown from a request handler. The only way I’ve found to shut down the server at another time is to send a request to yourself. For example, the /kill handler in this snippet will kill the dev server unless another request comes in during the next second:

import requests
from threading import Timer
from flask import request
import time

LAST_REQUEST_MS = 0
@app.before_request
def update_last_request_ms():
    global LAST_REQUEST_MS
    LAST_REQUEST_MS = time.time() * 1000


@app.route('/seriouslykill', methods=['POST'])
def seriouslykill():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    return "Shutting down..."


@app.route('/kill', methods=['POST'])
def kill():
    last_ms = LAST_REQUEST_MS
    def shutdown():
        if LAST_REQUEST_MS <= last_ms:  # subsequent requests abort shutdown
            requests.post('http://localhost:5000/seriouslykill')
        else:
            pass

    Timer(1.0, shutdown).start()  # wait 1 second
    return "Shutting down..."

回答 5

这是一个古老的问题,但是谷歌搜索并没有给我任何有关如何完成此操作的见识。

因为我在这里没有正确阅读代码!(Do!)它的作用是在……中RuntimeError不存在时引发一个。werkzeug.server.shutdownrequest.environ

因此,如果没有,我们可以做的request就是提高RuntimeError

def shutdown():
    raise RuntimeError("Server going down")

并在app.run()返回时捕捉到:

...
try:
    app.run(host="0.0.0.0")
except RuntimeError, msg:
    if str(msg) == "Server going down":
        pass # or whatever you want to do when the server goes down
    else:
        # appropriate handling/logging of other runtime errors
# and so on
...

无需向自己发送请求。

This is an old question, but googling didn’t give me any insight in how to accomplish this.

Because I didn’t read the code here properly! (Doh!) What it does is to raise a RuntimeError when there is no werkzeug.server.shutdown in the request.environ

So what we can do when there is no request is to raise a RuntimeError

def shutdown():
    raise RuntimeError("Server going down")

and catch that when app.run() returns:

...
try:
    app.run(host="0.0.0.0")
except RuntimeError, msg:
    if str(msg) == "Server going down":
        pass # or whatever you want to do when the server goes down
    else:
        # appropriate handling/logging of other runtime errors
# and so on
...

No need to send yourself a request.


回答 6

您不必按“ CTRL-C”,但是您可以提供一个可以为您执行此操作的端点:

from flask import Flask, jsonify, request
import json, os, signal

@app.route('/stopServer', methods=['GET'])
def stopServer():
    os.kill(os.getpid(), signal.SIGINT)
    return jsonify({ "success": True, "message": "Server is shutting down..." })

现在,您只需调用此端点即可正常关​​闭服务器:

curl localhost:5000/stopServer

You don’t have to press “CTRL-C”, but you can provide an endpoint which does it for you:

from flask import Flask, jsonify, request
import json, os, signal

@app.route('/stopServer', methods=['GET'])
def stopServer():
    os.kill(os.getpid(), signal.SIGINT)
    return jsonify({ "success": True, "message": "Server is shutting down..." })

Now you can just call this endpoint to gracefully shutdown the server:

curl localhost:5000/stopServer

回答 7

您可以使用以下方法

app.do_teardown_appcontext()

You can use method bellow

app.do_teardown_appcontext()

回答 8

如果您使用的是CLI,并且仅运行一个Flask应用程序/进程(或者,您只想终止系统上运行的任何 flask进程),则可以使用以下命令将其终止:

kill $(pgrep -f flask)

If you’re working on the CLI and only have one flask app/process running (or rather, you just want want to kill any flask process running on your system), you can kill it with:

kill $(pgrep -f flask)


回答 9

如果您不在请求响应处理之外,则仍然可以:

import os
import signal

sig = getattr(signal, "SIGKILL", signal.SIGTERM)
os.kill(os.getpid(), sig)

If you’re outside the request-response handling, you can still:

import os
import signal

sig = getattr(signal, "SIGKILL", signal.SIGTERM)
os.kill(os.getpid(), sig)

回答 10

Google Cloud VM实例+ Flask应用

我将Flask应用程序托管在Google Cloud Platform虚拟机上。我使用来启动应用程序,python main.py但是问题是ctrl + c无法停止服务器。

此命令$ sudo netstat -tulnp | grep :5000终止服务器。

默认情况下,我的Flask应用程序在端口5000上运行。

注意:我的VM实例正在Linux 9上运行。

为此。尚未针对其他平台进行测试。如果它也适用于其他版本,请随时进行更新或评论。

Google Cloud VM instance + Flask App

I hosted my Flask Application on Google Cloud Platform Virtual Machine. I started the app using python main.py But the problem was ctrl+c did not work to stop the server.

This command $ sudo netstat -tulnp | grep :5000 terminates the server.

My Flask app runs on port 5000 by default.

Note: My VM instance is running on Linux 9.

It works for this. Haven’t tested for other platforms. Feel free to update or comment if it works for other versions too.


回答 11

对于Windows,停止/杀死Flask服务器非常容易-

  1. 转到任务管理器
  2. 查找flask.exe
  3. 选择并结束流程

For Windows, it is quite easy to stop/kill flask server –

  1. Goto Task Manager
  2. Find flask.exe
  3. Select and End process

Flask的网址路由中是否支持正则表达式?

问题:Flask的网址路由中是否支持正则表达式?

我知道Flask具有int,float和path转换器,但是我们正在开发的应用程序的URL中具有更复杂的模式。

有没有办法像Django一样使用正则表达式?

I understand that Flask has the int, float and path converters, but the application we’re developing has more complex patterns in its URLs.

Is there a way we can use regular expressions, as in Django?


回答 0

即使Armin用可接受的答案击败了我,我仍然认为我会显示一个简短的示例,说明如何在Flask中实现正则表达式匹配器,以防万一有人想要如何完成此工作的示例。

from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)

class RegexConverter(BaseConverter):
    def __init__(self, url_map, *items):
        super(RegexConverter, self).__init__(url_map)
        self.regex = items[0]


app.url_map.converters['regex'] = RegexConverter

@app.route('/<regex("[abcABC0-9]{4,6}"):uid>-<slug>/')
def example(uid, slug):
    return "uid: %s, slug: %s" % (uid, slug)


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

该URL应返回200:http:// localhost:5000 / abc0-foo /

该网址应返回404:http:// localhost:5000 / abcd-foo /

Even though Armin beat me to the punch with an accepted answer I thought I’d show an abbreviated example of how I implemented a regex matcher in Flask just in case anyone wants a working example of how this could be done.

from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)

class RegexConverter(BaseConverter):
    def __init__(self, url_map, *items):
        super(RegexConverter, self).__init__(url_map)
        self.regex = items[0]


app.url_map.converters['regex'] = RegexConverter

@app.route('/<regex("[abcABC0-9]{4,6}"):uid>-<slug>/')
def example(uid, slug):
    return "uid: %s, slug: %s" % (uid, slug)


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

this URL should return with 200: http://localhost:5000/abc0-foo/

this URL should will return with 404: http://localhost:5000/abcd-foo/


回答 1

您可以挂钩匹配任意表达式的自定义转换器自定义转换器

from random import randrange
from werkzeug.routing import Rule, Map, BaseConverter, ValidationError

class BooleanConverter(BaseConverter):

    def __init__(self, url_map, randomify=False):
        super(BooleanConverter, self).__init__(url_map)
        self.randomify = randomify
        self.regex = '(?:yes|no|maybe)'

    def to_python(self, value):
        if value == 'maybe':
            if self.randomify:
                return not randrange(2)
            raise ValidationError()
        return value == 'yes'

    def to_url(self, value):
        return value and 'yes' or 'no'

url_map = Map([
    Rule('/vote/<bool:werkzeug_rocks>', endpoint='vote'),
    Rule('/vote/<bool(randomify=True):foo>', endpoint='foo')
], converters={'bool': BooleanConverter})

You can hook in custom converters that match for arbitrary expressions: Custom Converter

from random import randrange
from werkzeug.routing import Rule, Map, BaseConverter, ValidationError

class BooleanConverter(BaseConverter):

    def __init__(self, url_map, randomify=False):
        super(BooleanConverter, self).__init__(url_map)
        self.randomify = randomify
        self.regex = '(?:yes|no|maybe)'

    def to_python(self, value):
        if value == 'maybe':
            if self.randomify:
                return not randrange(2)
            raise ValidationError()
        return value == 'yes'

    def to_url(self, value):
        return value and 'yes' or 'no'

url_map = Map([
    Rule('/vote/<bool:werkzeug_rocks>', endpoint='vote'),
    Rule('/vote/<bool(randomify=True):foo>', endpoint='foo')
], converters={'bool': BooleanConverter})

回答 2

您还可以编写一条捕获所有类型的路由并在该方法内执行复杂的路由:

from flask import Flask
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'], defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST'])
def catch_all(path):
    return 'You want path: %s' % path

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

这将匹配任何请求。在此处查看更多详细信息:捕获所有URL

You could also write a catch all type of route and do complex routing within the method:

from flask import Flask
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'], defaults={'path': ''})
@app.route('/<path:path>', methods=['GET', 'POST'])
def catch_all(path):
    return 'You want path: %s' % path

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

This will match any request. See more details here: Catch-All URL.


如何在Flask中设置响应头?

问题:如何在Flask中设置响应头?

这是我的代码:

@app.route('/hello', methods=["POST"])
def hello():
    resp = make_response()
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

但是,当我从浏览器向服务器发出请求时,出现此错误:

XMLHttpRequest cannot load http://localhost:5000/hello. 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

我也尝试过这种方法,在请求之后设置响应头:

@app.after_request
def add_header(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

没有骰子。我犯了同样的错误。有没有一种方法可以只在route函数中设置响应头?这样的事情将是理想的:

@app.route('/hello', methods=["POST"])
    def hello(response): # is this a thing??
        response.headers['Access-Control-Allow-Origin'] = '*'
        return response

但我还是找不到这样做。请帮忙。

编辑

如果我使用POST请求卷曲网址,如下所示:

curl -iX POST http://localhost:5000/hello

我得到这个回应:

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/html
Content-Length: 291
Server: Werkzeug/0.9.6 Python/2.7.6
Date: Tue, 16 Sep 2014 03:58:42 GMT

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request.  Either the server is overloaded or there is an error in the application.</p>

有任何想法吗?

This is my code:

@app.route('/hello', methods=["POST"])
def hello():
    resp = make_response()
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

However, when I make a request from the browser to my server I get this error:

XMLHttpRequest cannot load http://localhost:5000/hello. 
No 'Access-Control-Allow-Origin' header is present on the requested resource.

I have also tried this approach, setting the response headers “after” the request:

@app.after_request
def add_header(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    return response

No dice. I get the same error. Is there a way to just set the response headers in the route function? Something like this would be ideal:

@app.route('/hello', methods=["POST"])
    def hello(response): # is this a thing??
        response.headers['Access-Control-Allow-Origin'] = '*'
        return response

but I cant find anyway to do this. Please help.

EDIT

if I curl the url with a POST request like so:

curl -iX POST http://localhost:5000/hello

I get this response:

HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/html
Content-Length: 291
Server: Werkzeug/0.9.6 Python/2.7.6
Date: Tue, 16 Sep 2014 03:58:42 GMT

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request.  Either the server is overloaded or there is an error in the application.</p>

Any ideas?


回答 0

您可以很容易地做到这一点:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

查看flask.Responseflask.make_response()

但是有些事情告诉我您还有另一个问题,因为after_request应当也正确地处理了它。

编辑
我刚刚注意到您已经在使用make_response它,这是执行此操作的方法之一。就像我之前说过的,after_request应该也可以。尝试通过curl到达端点,看看标题是什么:

curl -i http://127.0.0.1:5000/your/endpoint

你应该看到

> curl -i 'http://127.0.0.1:5000/'
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 11
Access-Control-Allow-Origin: *
Server: Werkzeug/0.8.3 Python/2.7.5
Date: Tue, 16 Sep 2014 03:47:13 GMT

注意Access-Control-Allow-Origin标头。

编辑2
正如我所怀疑的,您得到500,所以您没有按照您的想法设置标题。app.debug = True在启动应用程序之前尝试添加,然后重试。您应该获得一些输出,以显示问题的根本原因。

例如:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    user.weapon = boomerang
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

给出格式正确的html错误页面,该页面位于底部(有助于curl命令)

Traceback (most recent call last):
...
  File "/private/tmp/min.py", line 8, in home
    user.weapon = boomerang
NameError: global name 'boomerang' is not defined

You can do this pretty easily:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Look at flask.Response and flask.make_response()

But something tells me you have another problem, because the after_request should have handled it correctly too.

EDIT
I just noticed you are already using make_response which is one of the ways to do it. Like I said before, after_request should have worked as well. Try hitting the endpoint via curl and see what the headers are:

curl -i http://127.0.0.1:5000/your/endpoint

You should see

> curl -i 'http://127.0.0.1:5000/'
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 11
Access-Control-Allow-Origin: *
Server: Werkzeug/0.8.3 Python/2.7.5
Date: Tue, 16 Sep 2014 03:47:13 GMT

Noting the Access-Control-Allow-Origin header.

EDIT 2
As I suspected, you are getting a 500 so you are not setting the header like you thought. Try adding app.debug = True before you start the app and try again. You should get some output showing you the root cause of the problem.

For example:

@app.route("/")
def home():
    resp = flask.Response("Foo bar baz")
    user.weapon = boomerang
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

Gives a nicely formatted html error page, with this at the bottom (helpful for curl command)

Traceback (most recent call last):
...
  File "/private/tmp/min.py", line 8, in home
    user.weapon = boomerang
NameError: global name 'boomerang' is not defined

回答 1

使用make_responseFlask之类的东西

@app.route("/")
def home():
    resp = make_response("hello") #here you could use make_response(render_template(...)) too
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

flask文档

flask.make_response(* args)

有时有必要在视图中设置其他标题。由于视图不必返回响应对象,而是可以返回Flask本身将其转换为响应对象的值,因此向其添加标头变得很棘手。可以调用此函数而不是使用return,您将获得一个响应对象,可用于附加标题。

Use make_response of Flask something like

@app.route("/")
def home():
    resp = make_response("hello") #here you could use make_response(render_template(...)) too
    resp.headers['Access-Control-Allow-Origin'] = '*'
    return resp

From flask docs,

flask.make_response(*args)

Sometimes it is necessary to set additional headers in a view. Because views do not have to return response objects but can return a value that is converted into a response object by Flask itself, it becomes tricky to add headers to it. This function can be called instead of using a return and you will get a response object which you can use to attach headers.


回答 2

这对我有用

from flask import Flask
from flask import Response

app = Flask(__name__)

@app.route("/")
def home():
    return Response(headers={'Access-Control-Allow-Origin':'*'})

if __name__ == "__main__":
    app.run()

This work for me

from flask import Flask
from flask import Response

app = Flask(__name__)

@app.route("/")
def home():
    return Response(headers={'Access-Control-Allow-Origin':'*'})

if __name__ == "__main__":
    app.run()

回答 3

这就是在我的烧瓶应用程序中添加我的标头的方式,并且效果很好

@app.after_request
def add_header(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

This was how added my headers in my flask application and it worked perfectly

@app.after_request
def add_header(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    return response

回答 4

我们可以在Flask应用程序上下文中使用以下命令在Python Flask应用程序中设置响应标头: flask.g

这种在Flask应用程序上下文中使用设置响应标头的方法flask.g是线程安全的,可用于从任何应用程序文件设置自定义和动态属性,如果我们从任何帮助程序类设置自定义/动态响应标头,则该方法特别有用。也可以从任何其他文件(例如中间件等)进行访问,这flask.g是全局的,仅对该请求线程有效。

说我是否要从正在从此应用程序调用的另一个api / http调用中读取响应标头,然后提取任何内容并将其设置为该应用程序的响应标头。

示例代码:文件: helper.py

import flask
from flask import request, g
from multidict import CIMultiDict
from asyncio import TimeoutError as HttpTimeout
from aiohttp import ClientSession

    def _extract_response_header(response)
      """
      extracts response headers from response object 
      and stores that required response header in flask.g app context
      """
      headers = CIMultiDict(response.headers)
      if 'my_response_header' not in g:
        g.my_response_header= {}
        g.my_response_header['x-custom-header'] = headers['x-custom-header']


    async def call_post_api(post_body):
      """
      sample method to make post api call using aiohttp clientsession
      """
      try:
        async with ClientSession() as session:
          async with session.post(uri, headers=_headers, json=post_body) as response:
            responseResult = await response.read()
            _extract_headers(response, responseResult)
            response_text = await response.text()
      except (HttpTimeout, ConnectionError) as ex:
        raise HttpTimeout(exception_message)

文件: middleware.py

import flask
from flask import request, g

class SimpleMiddleWare(object):
    """
    Simple WSGI middleware
    """

    def __init__(self, app):
        self.app = app
        self._header_name = "any_request_header"

    def __call__(self, environ, start_response):
        """
        middleware to capture request header from incoming http request
        """
        request_id_header = environ.get(self._header_name)
        environ[self._header_name] = request_id_header

        def new_start_response(status, response_headers, exc_info=None):
            """
            set custom response headers
            """
            # set the request header as response header
            response_headers.append((self._header_name, request_id_header))
            # this is trying to access flask.g values set in helper class & set that as response header
            values = g.get(my_response_header, {})
            if values.get('x-custom-header'):
                response_headers.append(('x-custom-header', values.get('x-custom-header')))
            return start_response(status, response_headers, exc_info)

        return self.app(environ, new_start_response)

从主类调用中间件

文件: main.py

from flask import Flask
import asyncio
from gevent.pywsgi import WSGIServer
from middleware import SimpleMiddleWare

    app = Flask(__name__)
    app.wsgi_app = SimpleMiddleWare(app.wsgi_app)

We can set the response headers in Python Flask application using Flask application context using flask.g

This way of setting response headers in Flask application context using flask.g is thread safe and can be used to set custom & dynamic attributes from any file of application, this is especially helpful if we are setting custom/dynamic response headers from any helper class, that can also be accessed from any other file ( say like middleware, etc), this flask.g is global & valid for that request thread only.

Say if i want to read the response header from another api/http call that is being called from this app, and then extract any & set it as response headers for this app.

Sample Code: file: helper.py

import flask
from flask import request, g
from multidict import CIMultiDict
from asyncio import TimeoutError as HttpTimeout
from aiohttp import ClientSession

    def _extract_response_header(response)
      """
      extracts response headers from response object 
      and stores that required response header in flask.g app context
      """
      headers = CIMultiDict(response.headers)
      if 'my_response_header' not in g:
        g.my_response_header= {}
        g.my_response_header['x-custom-header'] = headers['x-custom-header']


    async def call_post_api(post_body):
      """
      sample method to make post api call using aiohttp clientsession
      """
      try:
        async with ClientSession() as session:
          async with session.post(uri, headers=_headers, json=post_body) as response:
            responseResult = await response.read()
            _extract_headers(response, responseResult)
            response_text = await response.text()
      except (HttpTimeout, ConnectionError) as ex:
        raise HttpTimeout(exception_message)

file: middleware.py

import flask
from flask import request, g

class SimpleMiddleWare(object):
    """
    Simple WSGI middleware
    """

    def __init__(self, app):
        self.app = app
        self._header_name = "any_request_header"

    def __call__(self, environ, start_response):
        """
        middleware to capture request header from incoming http request
        """
        request_id_header = environ.get(self._header_name)
        environ[self._header_name] = request_id_header

        def new_start_response(status, response_headers, exc_info=None):
            """
            set custom response headers
            """
            # set the request header as response header
            response_headers.append((self._header_name, request_id_header))
            # this is trying to access flask.g values set in helper class & set that as response header
            values = g.get(my_response_header, {})
            if values.get('x-custom-header'):
                response_headers.append(('x-custom-header', values.get('x-custom-header')))
            return start_response(status, response_headers, exc_info)

        return self.app(environ, new_start_response)

Calling the middleware from main class

file : main.py

from flask import Flask
import asyncio
from gevent.pywsgi import WSGIServer
from middleware import SimpleMiddleWare

    app = Flask(__name__)
    app.wsgi_app = SimpleMiddleWare(app.wsgi_app)

Python Flask有意的空响应

问题:Python Flask有意的空响应

有没有一种方法可以返回make_response()具有特定属性的响应(来自对象或类似对象),以便它不会再次呈现页面并且也不会执行任何其他操作。我正在尝试在服务器上运行代码而不生成任何输出

一个简单的“不返回”会生成:

ValueError: View function did not return a response

这应该是可能的,因为以下内容仅下载文件而不渲染模板:

myString = "First line of a document"
response = make_response(myString)
response.headers["Content-Disposition"] = "attachment; filename=myFile.txt"
return response

Is there a way to return a response (from make_response() object or similar) with certain properties so that it doesn’t render the page again and doesn’t do anything else either. I am trying to run a code on the server without generating any output

A simple ‘return None’ produces:

ValueError: View function did not return a response

This should be possible because the following only downloads a file and doesn’t render the template:

myString = "First line of a document"
response = make_response(myString)
response.headers["Content-Disposition"] = "attachment; filename=myFile.txt"
return response

回答 0

您回应的请求,HTTP服务器必须返回的东西。HTTP“空响应”响应为204 No Content

return ('', 204)

请注意,将文件返回到浏览器并不是一个空的响应,只是与HTML响应不同。

You are responding to a request, your HTTP server must return something. The HTTP ’empty response’ response is 204 No Content:

return ('', 204)

Note that returning a file to the browser is not an empty response, just different from a HTML response.


使用Flask / Jinja2将HTML传递到模板

问题:使用Flask / Jinja2将HTML传递到模板

我正在为Flask和SQLAlchemy构建一个管理员,我想使用来将不同输入的HTML传递给我的视图render_template。模板框架似乎会自动转义html,因此所有<“’>都将转换为html实体。如何禁用它以使HTML正确呈现?

I’m building an admin for Flask and SQLAlchemy, and I want to pass the HTML for the different inputs to my view using render_template. The templating framework seems to escape the html automatically, so all <“‘> are converted to html entities. How can I disable that so that the HTML renders correctly?


回答 0

理想的方法是

{{ something|safe }}

而不是完全关闭自动转义。

the ideal way is to

{{ something|safe }}

than completely turning off auto escaping.


回答 1

您还可以从代码中将其声明为HTML安全的:

from flask import Markup
value = Markup('<strong>The HTML String</strong>')

然后将该值传递给模板,而他们不必这样做|safe

You can also declare it HTML safe from the code:

from flask import Markup
value = Markup('<strong>The HTML String</strong>')

Then pass that value to the templates and they don’t have to |safe it.


回答 2

从jinja docs部分HTML Escaping

启用自动转义后,默认情况下所有内容都将转义,除非明确标记为安全的值。可以通过应用程序或使用安全过滤器在模板中对其进行标记。

例:

 <div class="info">
   {{data.email_content|safe}}
 </div>

From the jinja docs section HTML Escaping:

When automatic escaping is enabled everything is escaped by default except for values explicitly marked as safe. Those can either be marked by the application or in the template by using the |safe filter.

Example:

 <div class="info">
   {{data.email_content|safe}}
 </div>

回答 3

当您有很多不需要转义的变量时,可以使用一个autoescape块:

{% autoescape off %}
{{ something }}
{{ something_else }}
<b>{{ something_important }}</b>
{% endautoescape %}

When you have a lot of variables that don’t need escaping, you can use an autoescape block:

{% autoescape off %}
{{ something }}
{{ something_else }}
<b>{{ something_important }}</b>
{% endautoescape %}

回答 4

有些人似乎关闭了自动转义功能,这会带来安全风险可能会影响字符串显示。

如果您只想在字符串中插入一些换行符并将其转换为<br />,则可以使用jinja宏,例如:

{% macro linebreaks_for_string( the_string ) -%}
{% if the_string %}
{% for line in the_string.split('\n') %}
<br />
{{ line }}
{% endfor %}
{% else %}
{{ the_string }}
{% endif %}
{%- endmacro %}

在您的模板中只需使用

{{ linebreaks_for_string( my_string_in_a_variable ) }}

Some people seem to turn autoescape off which carries security risks to manipulate the string display.

If you only want to insert some linebreaks into a string and convert the linebreaks into <br />, then you could take a jinja macro like:

{% macro linebreaks_for_string( the_string ) -%}
{% if the_string %}
{% for line in the_string.split('\n') %}
<br />
{{ line }}
{% endfor %}
{% else %}
{{ the_string }}
{% endif %}
{%- endmacro %}

and in your template just call this with

{{ linebreaks_for_string( my_string_in_a_variable ) }}

Flask上下文堆栈的目的是什么?

问题:Flask上下文堆栈的目的是什么?

我一直在使用请求/应用程序上下文有一段时间,但没有完全了解它的工作原理或设计原因。当涉及到请求或应用程序上下文时,“堆栈”的目的是什么?这是两个单独的堆栈,还是都是一个堆栈的一部分?是将请求上下文压入堆栈,还是堆栈本身?我是否可以在彼此之上推送/弹出多个上下文?如果是这样,我为什么要这样做?

抱歉所有问题,但是阅读了请求上下文和应用程序上下文文档后,我仍然感到困惑。

I’ve been using the request/application context for some time without fully understanding how it works or why it was designed the way it was. What is the purpose of the “stack” when it comes to the request or application context? Are these two separate stacks, or are they both part of one stack? Is the request context pushed onto a stack, or is it a stack itself? Am I able to push/pop multiple contexts on top of eachother? If so, why would I want to do that?

Sorry for all the questions, but I’m still confused after reading the documentation for Request Context and Application Context.


回答 0

多个应用

在您意识到Flask可以拥有多个应用程序之前,应用程序上下文(及其用途)确实令人困惑。想象一下您想让一个WSGI Python解释器运行多个Flask应用程序的情况。我们不是在这里谈论蓝图,而是在谈论完全不同的Flask应用程序。

您可以按照“应用程序分派”示例中的Flask文档部分进行类似的设置:

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

请注意,有两个完全不同的Flask应用程序被创建为“前端”和“后端”。换句话说,Flask(...)应用程序构造函数被调用了两次,创建了Flask应用程序的两个实例。

语境

当您使用Flask时,通常会最终使用全局变量来访问各种功能。例如,您可能有读取如下代码。

from flask import request

然后,在查看期间,您可能会request用来访问当前请求的信息。显然,request这不是正常的全局变量;实际上,它是上下文局部值。换句话说,幕后有一些魔术说“当我打电话时request.pathpathrequestCURRENT请求的对象中获取属性”。两个不同的请求将对产生不同的结果request.path

实际上,即使您使用多个线程运行Flask,Flask也足够聪明,可以将请求对象隔离。这样,两个线程(每个线程处理一个不同的请求)就可以同时调用request.path并获取各自请求的正确信息。

把它放在一起

因此,我们已经看到Flask可以在同一个解释器中处理多个应用程序,并且由于Flask允许您使用“上下文本地”全局变量的方式,因此必须有某种机制来确定“当前” 请求是什么(为了做)之类的事情request.path

将这些想法放在一起,Flask必须有某种方法来确定“当前”应用程序是什么也应该有意义!

您可能还具有类似于以下内容的代码:

from flask import url_for

像我们的request示例一样,该url_for函数的逻辑依赖于当前环境。但是,在这种情况下,可以清楚地看到逻辑在很大程度上取决于哪个应用程序被视为“当前”应用程序。在上面显示的前端/后端示例中,“前端”和“后端”应用程序都可能具有“ /登录”路由,因此url_for('/login')应返回不同的内容,具体取决于视图是否正在处理针对前端或后端应用程序的请求。

要回答您的问题…

当涉及到请求或应用程序上下文时,“堆栈”的目的是什么?

从请求上下文文档中:

由于请求上下文在内部作为堆栈维护,因此可以多次推送和弹出。这对于实现内部重定向之类的东西非常方便。

换句话说,即使您通常在这些“当前”请求或“当前”应用程序堆栈中有0或1个项目,也可能会有更多或更多的项目。

给出的示例是您的请求将返回“内部重定向”结果的地方。假设某个用户请求A,但您想返回该用户B。在大多数情况下,您将向用户发出重定向,并将该用户指向资源B,这意味着该用户将运行第二个请求以获取B。稍微不同的处理方式是进行内部重定向,这意味着在处理A时,Flask将向自身发出对资源B的新请求,并将第二个请求的结果用作用户原始请求的结果。

这是两个单独的堆栈,还是都是一个堆栈的一部分?

它们是两个单独的堆栈。但是,这是一个实现细节。更重要的是没有堆栈,而是可以随时获取“当前”应用程序或请求(堆栈顶部)的事实。

是将请求上下文压入堆栈,还是堆栈本身?

“请求上下文”是“请求上下文堆栈”的一项。与“应用程序上下文”和“应用程序上下文堆栈”类似。

我是否可以在彼此之上推送/弹出多个上下文?如果是这样,我为什么要这样做?

在Flask应用程序中,通常不会这样做。内部重定向(如上所述)的一个示例。但是,即使在这种情况下,您可能最终也会让Flask处理新请求,因此Flask会为您完成所有推送/弹出操作。

但是,在某些情况下,您想自己操作堆栈。

在请求之外运行代码

人们遇到的一个典型问题是,他们使用Flask-SQLAlchemy扩展来使用如下所示的代码来建立SQL数据库和模型定义…

app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)

然后,他们在应从外壳程序运行的脚本中使用appdb值。例如,“ setup_tables.py”脚本…

from myapp import app, db

# Set up models
db.create_all()

在这种情况下,Flask-SQLAlchemy扩展了解app应用程序,但在create_all()此过程中将抛出错误,抱怨没有应用程序上下文。该错误是合理的;您从未告诉Flask在运行该create_all方法时应处理哪个应用程序。

您可能想知道为什么with app.app_context()在视图中运行类似的函数时最终不需要此调用。原因是Flask在处理实际的Web请求时已经为您处理了应用程序上下文的管理。该问题实际上仅出现在这些视图函数(或其他此类回调)之外,例如在一次性脚本中使用模型时。

解决方案是自己推送应用程序上下文,这可以通过以下方法完成:

from myapp import app, db

# Set up models
with app.app_context():
    db.create_all()

这将推送一个新的应用程序上下文(使用的应用程序app,请记住可能有多个应用程序)。

测试中

您想操纵堆栈的另一种情况是进行测试。您可以创建一个处理请求的单元测试,然后检查结果:

import unittest
from flask import request

class MyTest(unittest.TestCase):
    def test_thing(self):
        with app.test_request_context('/?next=http://example.com/') as ctx:
            # You can now view attributes on request context stack by using `request`.

        # Now the request context stack is empty

Multiple Apps

The application context (and its purpose) is indeed confusing until you realize that Flask can have multiple apps. Imagine the situation where you want to have a single WSGI Python interpreter run multiple Flask application. We’re not talking Blueprints here, we’re talking entirely different Flask applications.

You might set this up similar to the Flask documentation section on “Application Dispatching” example:

from werkzeug.wsgi import DispatcherMiddleware
from frontend_app import application as frontend
from backend_app import application as backend

application = DispatcherMiddleware(frontend, {
    '/backend':     backend
})

Notice that there are two completely different Flask applications being created “frontend” and “backend”. In other words, the Flask(...) application constructor has been called twice, creating two instances of a Flask application.

Contexts

When you are working with Flask, you often end up using global variables to access various functionality. For example, you probably have code that reads…

from flask import request

Then, during a view, you might use request to access the information of the current request. Obviously, request is not a normal global variable; in actuality, it is a context local value. In other words, there is some magic behind the scenes that says “when I call request.path, get the path attribute from the request object of the CURRENT request.” Two different requests will have a different results for request.path.

In fact, even if you run Flask with multiple threads, Flask is smart enough to keep the request objects isolated. In doing so, it becomes possible for two threads, each handling a different request, to simultaneously call request.path and get the correct information for their respective requests.

Putting it Together

So we’ve already seen that Flask can handle multiple applications in the same interpreter, and also that because of the way that Flask allows you to use “context local” globals there must be some mechanism to determine what the “current” request is (in order to do things such as request.path).

Putting these ideas together, it should also make sense that Flask must have some way to determine what the “current” application is!

You probably also have code similar to the following:

from flask import url_for

Like our request example, the url_for function has logic that is dependent on the current environment. In this case, however, it is clear to see that the logic is heavily dependent on which app is considered the “current” app. In the frontend/backend example shown above, both the “frontend” and “backend” apps could have a “/login” route, and so url_for('/login') should return something different depending on if the view is handling the request for the frontend or backend app.

To answer your questions…

What is the purpose of the “stack” when it comes to the request or application context?

From the Request Context docs:

Because the request context is internally maintained as a stack you can push and pop multiple times. This is very handy to implement things like internal redirects.

In other words, even though you typically will have 0 or 1 items on these stack of “current” requests or “current” applications, it is possible that you could have more.

The example given is where you would have your request return the results of an “internal redirect”. Let’s say a user requests A, but you want to return to the user B. In most cases, you issue a redirect to the user, and point the user to resource B, meaning the user will run a second request to fetch B. A slightly different way of handling this would be to do an internal redirect, which means that while processing A, Flask will make a new request to itself for resource B, and use the results of this second request as the results of the user’s original request.

Are these two separate stacks, or are they both part of one stack?

They are two separate stacks. However, this is an implementation detail. What’s more important is not so much that there is a stack, but the fact that at any time you can get the “current” app or request (top of the stack).

Is the request context pushed onto a stack, or is it a stack itself?

A “request context” is one item of the “request context stack”. Similarly with the “app context” and “app context stack”.

Am I able to push/pop multiple contexts on top of eachother? If so, why would I want to do that?

In a Flask application, you typically would not do this. One example of where you might want to is for an internal redirect (described above). Even in that case, however, you would probably end up having Flask handle a new request, and so Flask would do all of the pushing/popping for you.

However, there are some cases where you’d want to manipulate the stack yourself.

Running code outside of a request

One typical problem people have is that they use the Flask-SQLAlchemy extension to set up a SQL database and model definition using code something like what is shown below…

app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)

Then they use the app and db values in a script that should be run from the shell. For example, a “setup_tables.py” script…

from myapp import app, db

# Set up models
db.create_all()

In this case, the Flask-SQLAlchemy extension knows about the app application, but during create_all() it will throw an error complaining about there not being an application context. This error is justified; you never told Flask what application it should be dealing with when running the create_all method.

You might be wondering why you don’t end up needing this with app.app_context() call when you run similar functions in your views. The reason is that Flask already handles the management of the application context for you when it is handling actual web requests. The problem really only comes up outside of these view functions (or other such callbacks), such as when using your models in a one-off script.

The resolution is to push the application context yourself, which can be done by doing…

from myapp import app, db

# Set up models
with app.app_context():
    db.create_all()

This will push a new application context (using the application of app, remember there could be more than one application).

Testing

Another case where you would want to manipulate the stack is for testing. You could create a unit test that handles a request and you check the results:

import unittest
from flask import request

class MyTest(unittest.TestCase):
    def test_thing(self):
        with app.test_request_context('/?next=http://example.com/') as ctx:
            # You can now view attributes on request context stack by using `request`.

        # Now the request context stack is empty

回答 1

先前的答案已经很好地概述了在请求期间Flask后台发生的情况。如果您还没有阅读它,我建议您在阅读之前先@MarkHildreth的答案。简而言之,将为每个http请求创建一个新的上下文(线程),这就是为什么必须要有一个Local允许诸如request和的g可以跨线程全局访问,同时保持它们的请求特定上下文。此外,在处理http请求时,Flask可以从内部模拟其他请求,因此有必要将它们各自的上下文存储在堆栈中。另外,Flask允许多个wsgi应用程序在单个进程中相互运行,并且在请求期间可以调用多个(每个请求都创建一个新的应用程序上下文)以执行操作,因此需要应用程序上下文堆栈。这是先前答案中所涵盖内容的摘要。

我现在的目标是通过解释Flask和Werkzeug 如何处理这些上下文本地人来补充我们目前的理解。我简化了代码以增强对其逻辑的理解,但是,如果理解了这一点,则应该能够轻松掌握实际源代码(werkzeug.localflask.globals)中的大部分内容。

首先让我们了解一下Werkzeug如何实现线程Locals。

本地

当http请求进入时,将在单个线程的上下文中对其进行处理。作为在http请求期间生成新上下文的一种替代方法,Werkzeug还允许使用greenlets(一种较轻的“微线程”)代替普通线程。如果您没有安装greenlet,它将恢复为使用线程。这些线程(或Greenlet)中的每一个都可以通过唯一的ID进行标识,您可以使用模块的get_ident()功能进行检索。这个函数是出发点,以神奇的背后有requestcurrent_appurl_forg,等这样的背景下,结合全局对象。

try:
    from greenlet import get_ident
except ImportError:
    from thread import get_ident

现在我们有了身份函数,我们可以随时知道我们在哪个线程上,并且可以创建所谓的线程 Local的上下文对象,该对象可以全局访问,但是当您访问其属性时,它们将解析为它们的值该特定线程。例如

# globally
local = Local()

# ...

# on thread 1
local.first_name = 'John'

# ...

# on thread 2
local.first_name = 'Debbie'

这两个值同时存在于全局可访问Local对象上,但访问local.first_name在线程1的上下文中进行将为您提供'John',而'Debbie'在线程2 上将返回。

那怎么可能?让我们看一些(简化的)代码:

class Local(object)
    def __init__(self):
        self.storage = {}

    def __getattr__(self, name):
        context_id = get_ident() # we get the current thread's or greenlet's id
        contextual_storage = self.storage.setdefault(context_id, {})
        try:
            return contextual_storage[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        context_id = get_ident()
        contextual_storage = self.storage.setdefault(context_id, {})
        contextual_storage[name] = value

    def __release_local__(self):
        context_id = get_ident()
        self.storage.pop(context_id, None)

local = Local()

从上面的代码中,我们可以看到魔术归结为 get_ident()为当前的greenlet或线程。然后,Local存储仅将其用作密钥来存储与当前线程相关的任何数据。

Local每个流程和request,可以有多个对象gcurrent_app而其他对象就可以像这样简单地创建。但这不是Flask的工作方式,在技术上这些都不是 Local对象对象,而是更准确的LocalProxy对象。什么LocalProxy

本地代理

LocalProxy是一个查询的对象,Local以查找另一个感兴趣的对象(即它代理的对象)。让我们来了解一下:

class LocalProxy(object):
    def __init__(self, local, name):
        # `local` here is either an actual `Local` object, that can be used
        # to find the object of interest, here identified by `name`, or it's
        # a callable that can resolve to that proxied object
        self.local = local
        # `name` is an identifier that will be passed to the local to find the
        # object of interest.
        self.name = name

    def _get_current_object(self):
        # if `self.local` is truly a `Local` it means that it implements
        # the `__release_local__()` method which, as its name implies, is
        # normally used to release the local. We simply look for it here
        # to identify which is actually a Local and which is rather just
        # a callable:
        if hasattr(self.local, '__release_local__'):
            try:
                return getattr(self.local, self.name)
            except AttributeError:
                raise RuntimeError('no object bound to %s' % self.name)

        # if self.local is not actually a Local it must be a callable that 
        # would resolve to the object of interest.
        return self.local(self.name)

    # Now for the LocalProxy to perform its intended duties i.e. proxying 
    # to an underlying object located somewhere in a Local, we turn all magic
    # methods into proxies for the same methods in the object of interest.
    @property
    def __dict__(self):
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError('__dict__')

    def __repr__(self):
        try:
            return repr(self._get_current_object())
        except RuntimeError:
            return '<%s unbound>' % self.__class__.__name__

    def __bool__(self):
        try:
            return bool(self._get_current_object())
        except RuntimeError:
            return False

    # ... etc etc ... 

    def __getattr__(self, name):
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

    def __setitem__(self, key, value):
        self._get_current_object()[key] = value

    def __delitem__(self, key):
        del self._get_current_object()[key]

    # ... and so on ...

    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    __str__ = lambda x: str(x._get_current_object())
    __lt__ = lambda x, o: x._get_current_object() < o
    __le__ = lambda x, o: x._get_current_object() <= o
    __eq__ = lambda x, o: x._get_current_object() == o

    # ... and so forth ...

现在要创建可全局访问的代理

# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')

现在,在请求过程的早期,您将在本地创建的对象中存储一些对象,无论我们使用哪个线程,以前创建的代理都可以访问这些对象

# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()

将其LocalProxy用作全局可访问对象而不是使它们成为Locals自己的优点是简化了它们的管理。您只需要一个Local对象即可创建许多可全局访问的代理。在请求结束时,在清理过程中,您只需释放一个Local(即,从其存储中弹出context_id),而不必理会代理,它们仍可全局访问,并且仍然依赖于代理Local来查找其对象对后续的http请求感兴趣。

# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()

为了简化LocalProxy已经存在的创建Local,Werkzeug实现了Local.__call__()magic方法,如下所示:

class Local(object):
    # ... 
    # ... all same stuff as before go here ...
    # ... 

    def __call__(self, name):
        return LocalProxy(self, name)

# now you can do
local = Local()
request = local('request')
g = local('g')

但是,如果你在烧瓶来源看(flask.globals)这仍然不是如何requestgcurrent_appsession创建。正如我们已经建立的那样,Flask可以产生多个“假”请求(来自单个真实的http请求),并且在此过程中还可以推送多个应用程序上下文。这不是常见的用例,但是是框架的功能。由于这些“并发”请求和应用仍被限制为仅在任何时候都只有一个具有“焦点”的情况下运行,因此将堆栈用于其各自的上下文是有意义的。每当产生新请求或调用一个应用程序时,它们就会将上下文推入各自堆栈的顶部。Flask LocalStack为此目的使用对象。当他们结束业务时,他们将上下文弹出堆栈。

本地堆栈

这是一个LocalStack看起来像(再次代码被简化,以方便其逻辑的理解)。

class LocalStack(object):

    def __init__(self):
        self.local = Local()

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self.local, 'stack', None)
        if rv is None:
            self.local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self.local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self.local) # this simply releases the local
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self):
        """The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
        try:
            return self.local.stack[-1]
        except (AttributeError, IndexError):
            return None

请注意,从上面看,a LocalStack是存储在本地中的堆栈,而不是存储在堆栈中的一堆本地。这意味着尽管堆栈可以全局访问,但每个线程中的堆栈都是不同的。

瓶没有它requestcurrent_appg,和session物品直接解决的LocalStack,它,而使用LocalProxy对象包装查找功能(而不是Local对象)会发现从底层对象LocalStack

_request_ctx_stack = LocalStack()
def _find_request():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.request
request = LocalProxy(_find_request)

def _find_session():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.session
session = LocalProxy(_find_session)

_app_ctx_stack = LocalStack()
def _find_g():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.g
g = LocalProxy(_find_g)

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.app
current_app = LocalProxy(_find_app)

所有这些都是在应用程序启动时声明的,但是直到将请求上下文或应用程序上下文推入其各自的堆栈之前,它们实际上都不会解析为任何东西。

如果您想知道上下文是如何实际插入堆栈中(然后弹出)的,请查看flask.app.Flask.wsgi_app()wsgi应用程序的进入点(即Web服务器调用什么并将http环境传递给何时)。请求进入),并按照创建RequestContext对象都通过其随后push()进入_request_ctx_stack。一旦推送到堆栈的顶部,就可以通过以下方式访问它_request_ctx_stack.top。以下是一些简短的代码来演示流程:

因此,您启动了一个应用程序并将其提供给WSGI服务器使用…

app = Flask(*config, **kwconfig)

# ...

后来有一个http请求进入,WSGI服务器使用通常的参数调用该应用程序。

app(environ, start_response) # aka app.__call__(environ, start_response)

这大致就是应用程序中发生的事情…

def Flask(object):

    # ...

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def wsgi_app(self, environ, start_response):
        ctx = RequestContext(self, environ)
        ctx.push()
        try:
            # process the request here
            # raise error if any
            # return Response
        finally:
            ctx.pop()

    # ...

这大致就是RequestContext发生的情况…

class RequestContext(object):

    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()
        self.flashes = None

    def push(self):
        _request_ctx_stack.push(self)

    def pop(self):
        _request_ctx_stack.pop()

假设请求已完成初始化,那么request.path从您的一个视图函数中查找的内容将如下所示:

  • 从全局可访问LocalProxy对象开始request
  • 要找到其感兴趣的基础对象(代理的对象),它将调用其查找功能_find_request()(注册为该功能的功能)self.local)。
  • 该函数查询LocalStack对象_request_ctx_stack的堆栈顶部上下文。
  • 为了找到顶部上下文,LocalStack对象首先在其内部Local属性(self.local)中查询stack先前存储在此处属性。
  • 来自 stack获得顶级上下文
  • top.request因此被解析为感兴趣的底层对象。
  • 从那个对象我们得到path属性

因此,我们已经了解了LocalLocalProxy和如何LocalStack工作,现在思考一下path从中检索的含义和细微差别:

  • 一个request可能是简单的全局可访问对象的对象。
  • 一个request对象,这将是一个地方。
  • 一个request对象存储为本地的属性。
  • 一个request对象,它是存储在本地对象的代理。
  • request存储在堆栈中的对象,该对象又存储在本地中。
  • 一个request对象,它是存储在本地的堆栈上的对象的代理。<-这就是Flask所做的。

Previous answers already give a nice overview of what goes on in the background of Flask during a request. If you haven’t read it yet I recommend @MarkHildreth’s answer prior to reading this. In short, a new context (thread) is created for each http request, which is why it’s necessary to have a thread Local facility that allows objects such as request and g to be accessible globally across threads, while maintaining their request specific context. Furthermore, while processing an http request Flask can emulate additional requests from within, hence the necessity to store their respective context on a stack. Also, Flask allows multiple wsgi applications to run along each other within a single process, and more than one can be called to action during a request (each request creates a new application context), hence the need for a context stack for applications. That’s a summary of what was covered in previous answers.

My goal now is to complement our current understanding by explaining how Flask and Werkzeug do what they do with these context locals. I simplified the code to enhance the understanding of its logic, but if you get this, you should be able to easily grasp most of what’s in the actual source (werkzeug.local and flask.globals).

Let’s first understand how Werkzeug implements thread Locals.

Local

When an http request comes in, it is processed within the context of a single thread. As an alternative mean to spawn a new context during an http request, Werkzeug also allows the use of greenlets (a sort of lighter “micro-threads”) instead of normal threads. If you don’t have greenlets installed it will revert to using threads instead. Each of these threads (or greenlets) are identifiable by a unique id, which you can retrieve with the module’s get_ident() function. That function is the starting point to the magic behind having request, current_app,url_for, g, and other such context-bound global objects.

try:
    from greenlet import get_ident
except ImportError:
    from thread import get_ident

Now that we have our identity function we can know which thread we’re on at any given time and we can create what’s called a thread Local, a contextual object that can be accessed globally, but when you access its attributes they resolve to their value for that specific thread. e.g.

# globally
local = Local()

# ...

# on thread 1
local.first_name = 'John'

# ...

# on thread 2
local.first_name = 'Debbie'

Both values are present on the globally accessible Local object at the same time, but accessing local.first_name within the context of thread 1 will give you 'John', whereas it will return 'Debbie' on thread 2.

How is that possible? Let’s look at some (simplified) code:

class Local(object)
    def __init__(self):
        self.storage = {}

    def __getattr__(self, name):
        context_id = get_ident() # we get the current thread's or greenlet's id
        contextual_storage = self.storage.setdefault(context_id, {})
        try:
            return contextual_storage[name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        context_id = get_ident()
        contextual_storage = self.storage.setdefault(context_id, {})
        contextual_storage[name] = value

    def __release_local__(self):
        context_id = get_ident()
        self.storage.pop(context_id, None)

local = Local()

From the code above we can see that the magic boils down to get_ident() which identifies the current greenlet or thread. The Local storage then just uses that as a key to store any data contextual to the current thread.

You can have multiple Local objects per process and request, g, current_app and others could simply have been created like that. But that’s not how it’s done in Flask in which these are not technically Local objects, but more accurately LocalProxy objects. What’s a LocalProxy?

LocalProxy

A LocalProxy is an object that queries a Local to find another object of interest (i.e. the object it proxies to). Let’s take a look to understand:

class LocalProxy(object):
    def __init__(self, local, name):
        # `local` here is either an actual `Local` object, that can be used
        # to find the object of interest, here identified by `name`, or it's
        # a callable that can resolve to that proxied object
        self.local = local
        # `name` is an identifier that will be passed to the local to find the
        # object of interest.
        self.name = name

    def _get_current_object(self):
        # if `self.local` is truly a `Local` it means that it implements
        # the `__release_local__()` method which, as its name implies, is
        # normally used to release the local. We simply look for it here
        # to identify which is actually a Local and which is rather just
        # a callable:
        if hasattr(self.local, '__release_local__'):
            try:
                return getattr(self.local, self.name)
            except AttributeError:
                raise RuntimeError('no object bound to %s' % self.name)

        # if self.local is not actually a Local it must be a callable that 
        # would resolve to the object of interest.
        return self.local(self.name)

    # Now for the LocalProxy to perform its intended duties i.e. proxying 
    # to an underlying object located somewhere in a Local, we turn all magic
    # methods into proxies for the same methods in the object of interest.
    @property
    def __dict__(self):
        try:
            return self._get_current_object().__dict__
        except RuntimeError:
            raise AttributeError('__dict__')

    def __repr__(self):
        try:
            return repr(self._get_current_object())
        except RuntimeError:
            return '<%s unbound>' % self.__class__.__name__

    def __bool__(self):
        try:
            return bool(self._get_current_object())
        except RuntimeError:
            return False

    # ... etc etc ... 

    def __getattr__(self, name):
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)

    def __setitem__(self, key, value):
        self._get_current_object()[key] = value

    def __delitem__(self, key):
        del self._get_current_object()[key]

    # ... and so on ...

    __setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
    __delattr__ = lambda x, n: delattr(x._get_current_object(), n)
    __str__ = lambda x: str(x._get_current_object())
    __lt__ = lambda x, o: x._get_current_object() < o
    __le__ = lambda x, o: x._get_current_object() <= o
    __eq__ = lambda x, o: x._get_current_object() == o

    # ... and so forth ...

Now to create globally accessible proxies you would do

# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')

and now some time early over the course of a request you would store some objects inside the local that the previously created proxies can access, no matter which thread we’re on

# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()

The advantage of using LocalProxy as globally accessible objects rather than making them Locals themselves is that it simplifies their management. You only just need a single Local object to create many globally accessible proxies. At the end of the request, during cleanup, you simply release the one Local (i.e. you pop the context_id from its storage) and don’t bother with the proxies, they’re still globally accessible and still defer to the one Local to find their object of interest for subsequent http requests.

# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()

To simplify the creation of a LocalProxy when we already have a Local, Werkzeug implements the Local.__call__() magic method as follows:

class Local(object):
    # ... 
    # ... all same stuff as before go here ...
    # ... 

    def __call__(self, name):
        return LocalProxy(self, name)

# now you can do
local = Local()
request = local('request')
g = local('g')

However, if you look in the Flask source (flask.globals) that’s still not how request, g, current_app and session are created. As we’ve established, Flask can spawn multiple “fake” requests (from a single true http request) and in the process also push multiple application contexts. This isn’t a common use-case, but it’s a capability of the framework. Since these “concurrent” requests and apps are still limited to run with only one having the “focus” at any time, it makes sense to use a stack for their respective context. Whenever a new request is spawned or one of the applications is called, they push their context at the top of their respective stack. Flask uses LocalStack objects for this purpose. When they conclude their business they pop the context out of the stack.

LocalStack

This is what a LocalStack looks like (again the code is simplified to facilitate understanding of its logic).

class LocalStack(object):

    def __init__(self):
        self.local = Local()

    def push(self, obj):
        """Pushes a new item to the stack"""
        rv = getattr(self.local, 'stack', None)
        if rv is None:
            self.local.stack = rv = []
        rv.append(obj)
        return rv

    def pop(self):
        """Removes the topmost item from the stack, will return the
        old value or `None` if the stack was already empty.
        """
        stack = getattr(self.local, 'stack', None)
        if stack is None:
            return None
        elif len(stack) == 1:
            release_local(self.local) # this simply releases the local
            return stack[-1]
        else:
            return stack.pop()

    @property
    def top(self):
        """The topmost item on the stack.  If the stack is empty,
        `None` is returned.
        """
        try:
            return self.local.stack[-1]
        except (AttributeError, IndexError):
            return None

Note from the above that a LocalStack is a stack stored in a local, not a bunch of locals stored on a stack. This implies that although the stack is globally accessible it’s a different stack in each thread.

Flask doesn’t have its request, current_app, g, and session objects resolving directly to a LocalStack, it rather uses LocalProxy objects that wrap a lookup function (instead of a Local object) that will find the underlying object from the LocalStack:

_request_ctx_stack = LocalStack()
def _find_request():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.request
request = LocalProxy(_find_request)

def _find_session():
    top = _request_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of request context')
    return top.session
session = LocalProxy(_find_session)

_app_ctx_stack = LocalStack()
def _find_g():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.g
g = LocalProxy(_find_g)

def _find_app():
    top = _app_ctx_stack.top
    if top is None:
        raise RuntimeError('working outside of application context')
    return top.app
current_app = LocalProxy(_find_app)

All these are declared at application start-up, but do not actually resolve to anything until a request context or application context is pushed to their respective stack.

If you’re curious to see how a context is actually inserted in the stack (and subsequently popped out), look in flask.app.Flask.wsgi_app() which is the point of entry of the wsgi app (i.e. what the web server calls and pass the http environment to when a request comes in), and follow the creation of the RequestContext object all through its subsequent push() into _request_ctx_stack. Once pushed at the top of the stack, it’s accessible via _request_ctx_stack.top. Here’s some abbreviated code to demonstrate the flow:

So you start an app and make it available to the WSGI server…

app = Flask(*config, **kwconfig)

# ...

Later an http request comes in and the WSGI server calls the app with the usual params…

app(environ, start_response) # aka app.__call__(environ, start_response)

This is roughly what happens in the app…

def Flask(object):

    # ...

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def wsgi_app(self, environ, start_response):
        ctx = RequestContext(self, environ)
        ctx.push()
        try:
            # process the request here
            # raise error if any
            # return Response
        finally:
            ctx.pop()

    # ...

and this is roughly what happens with RequestContext…

class RequestContext(object):

    def __init__(self, app, environ, request=None):
        self.app = app
        if request is None:
            request = app.request_class(environ)
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.session = self.app.open_session(self.request)
        if self.session is None:
            self.session = self.app.make_null_session()
        self.flashes = None

    def push(self):
        _request_ctx_stack.push(self)

    def pop(self):
        _request_ctx_stack.pop()

Say a request has finished initializing, the lookup for request.path from one of your view functions would therefore go as follow:

  • start from the globally accessible LocalProxy object request.
  • to find its underlying object of interest (the object it’s proxying to) it calls its lookup function _find_request() (the function it registered as its self.local).
  • that function queries the LocalStack object _request_ctx_stack for the top context on the stack.
  • to find the top context, the LocalStack object first queries its inner Local attribute (self.local) for the stack property that was previously stored there.
  • from the stack it gets the top context
  • and top.request is thus resolved as the underlying object of interest.
  • from that object we get the path attribute

So we’ve seen how Local, LocalProxy, and LocalStack work, now think for a moment of the implications and nuances in retrieving the path from:

  • a request object that would be a simple globally accessible object.
  • a request object that would be a local.
  • a request object stored as an attribute of a local.
  • a request object that is a proxy to an object stored in a local.
  • a request object stored on a stack, that is in turn stored in a local.
  • a request object that is a proxy to an object on a stack stored in a local. <- this is what Flask does.

回答 2

@Mark Hildreth的答案很少。

上下文堆栈看起来像{thread.get_ident(): []},在这里[]称为“堆栈”,因为仅用于appendpushpop[-1]__getitem__(-1))操作。因此上下文堆栈将保留线程或greenlet线程的实际数据。

current_appgrequestsession和等是LocalProxy刚刚overrided特殊的方法对象__getattr____getitem____call____eq__等,并从上下文堆栈顶部(返回值[-1])的参数名(current_apprequest例如)。 LocalProxy需要一次导入此对象,并且它们不会丢失实际情况。所以最好只是导入request在代码中的任何地方,而不是将请求参数发送给您的函数和方法。您可以使用它轻松编写自己的扩展名,但不要忘记,琐碎的用法会使代码难以理解。

花时间了解https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py

那么如何填充两个堆栈?根据要求Flask

  1. request_context按环境创建(init map_adapter,匹配路径)
  2. 输入或推送此请求:
    1. 清除上一个 request_context
    2. 创建app_context是否丢失并推送到应用程序上下文堆栈
    3. 此请求已推送到请求上下文堆栈
    4. 初始化会话,如果错过了
  3. 派遣请求
  4. 清除请求并从堆栈中弹出

Little addition @Mark Hildreth‘s answer.

Context stack look like {thread.get_ident(): []}, where [] called “stack” because used only append (push), pop and [-1] (__getitem__(-1)) operations. So context stack will keep actual data for thread or greenlet thread.

current_app, g, request, session and etc is LocalProxy object which just overrided special methods __getattr__, __getitem__, __call__, __eq__ and etc. and return value from context stack top ([-1]) by argument name (current_app, request for example). LocalProxy needed to import this objects once and they will not miss actuality. So better just import request where ever you are in code instead play with sending request argument down to you functions and methods. You can easy write own extensions with it, but do not forget that frivolous usage can make code more difficult for understanding.

Spend time to understand https://github.com/mitsuhiko/werkzeug/blob/master/werkzeug/local.py.

So how populated both stacks? On request Flask:

  1. create request_context by environment (init map_adapter, match path)
  2. enter or push this request:
    1. clear previous request_context
    2. create app_context if it missed and pushed to application context stack
    3. this request pushed to request context stack
    4. init session if it missed
  3. dispatch request
  4. clear request and pop it from stack

回答 3

让我们举一个例子,假设您要设置一个用户上下文(使用Local和LocalProxy的flask构造)。

定义一个User类:

class User(object):
    def __init__(self):
        self.userid = None

定义一个函数来检索当前线程或greenlet中的用户对象

def get_user(_local):
    try:
        # get user object in current thread or greenlet
        return _local.user
    except AttributeError:
        # if user object is not set in current thread ,set empty user object 
       _local.user = User()
    return _local.user

现在定义一个LocalProxy

usercontext = LocalProxy(partial(get_user, Local()))

现在在当前线程usercontext.userid中获取用户的userid

说明:

1.Local具有identity和objet dict,identity是threadid或greenlet id,在此示例中,_local.user = User()与_local等效。___storage __ [当前线程的id] [“ user”] = User()

  1. LocalProxy 操作委托给包装好的本地对象,或者您可以提供一个返回目标对象的函数。在上面的示例中,get_user函数将当前用户对象提供给LocalProxy,当您通过usercontext.userid要求当前用户的userid时,LocalProxy的__getattr__函数首先调用get_user以获取User对象(用户),然后调用getattr(user,“ userid”)。只需在用户(在当前线程或greenlet中)上设置userid即可:usercontext.userid =“ user_123”

Lets take one example , suppose you want to set a usercontext (using flask construct of Local and LocalProxy).

Define one User class :

class User(object):
    def __init__(self):
        self.userid = None

define a function to retrive user object inside current thread or greenlet

def get_user(_local):
    try:
        # get user object in current thread or greenlet
        return _local.user
    except AttributeError:
        # if user object is not set in current thread ,set empty user object 
       _local.user = User()
    return _local.user

Now define a LocalProxy

usercontext = LocalProxy(partial(get_user, Local()))

Now to get userid of user in current thread usercontext.userid

explanation :

1.Local has a dict of identity and objet , identity is threadid or greenlet id , in this example _local.user = User() is eqivalent to _local.___storage__[current thread’s id] [“user”] = User()

  1. LocalProxy delegates operation to wrapped up Local object or you can provide a function that returns target object. In above example get_user function provides current user object to LocalProxy , and when you ask for current user’s userid by usercontext.userid, LocalProxy’s __getattr__ function first calls get_user to get User object (user) and then calls getattr(user,”userid”). to set userid on User ( in current thread or greenlet) you simply do : usercontext.userid = “user_123”

如何将Flask应用划分为多个py文件?

问题:如何将Flask应用划分为多个py文件?

我的flask应用程序当前包含一个test.py具有多个路由和已main()定义路由的文件。有什么办法可以创建一个test2.py文件,其中包含未处理的路由test.py

@app.route('/somepath')
def somehandler():
    # Handler code here

我担心其中包含太多路由,test.py并且希望使其运行python test.py,这样我也可以test.py像使用同一文件一样提取这些路由。为了使此功能正常运行,我必须进行哪些更改test.py和/或进行哪些更改test2.py

My flask application currently consists of a single test.py file with multiple routes and the main() route defined. Is there some way I could create a test2.py file that contains routes that were not handled in test.py?

@app.route('/somepath')
def somehandler():
    # Handler code here

I am concerned that there are too many routes in test.py and would like to make it such that I can run python test.py, which will also pick up the routes on test.py as if it were part of the same file. What changes to I have to make in test.py and/or include in test2.py to get this to work?


回答 0

您可以使用常规的Python包结构将您的应用分为多个模块,请参见Flask文档。

然而,

Flask使用蓝图的概念来制作应用程序组件并支持应用程序内或跨应用程序的通用模式。

您可以在单独的文件中将应用程序的子组件创建为蓝图:

simple_page = Blueprint('simple_page', __name__, template_folder='templates')
@simple_page.route('/<page>')
def show(page):
    # stuff

然后在主要部分中使用它:

from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

蓝图还可以捆绑特定资源:模板或静态文件。请参阅Flask文档以获取所有详细信息。

You can use the usual Python package structure to divide your App into multiple modules, see the Flask docs.

However,

Flask uses a concept of blueprints for making application components and supporting common patterns within an application or across applications.

You can create a sub-component of your app as a Blueprint in a separate file:

simple_page = Blueprint('simple_page', __name__, template_folder='templates')
@simple_page.route('/<page>')
def show(page):
    # stuff

And then use it in the main part:

from yourapplication.simple_page import simple_page

app = Flask(__name__)
app.register_blueprint(simple_page)

Blueprints can also bundle specific resources: templates or static files. Please refer to the Flask docs for all the details.


回答 1

我想推荐GitHub上的flask-empty

它提供了一种理解蓝图,多个视图和扩展的简便方法。

I would like to recommend flask-empty at GitHub.

It provides an easy way to understand Blueprints, multiple views and extensions.


回答 2

您可以使用简单的技巧,即从另一个文件内的main导入flask应用程序变量,例如:

test-routes.py

from __main__ import app

@app.route('/test', methods=['GET'])
def test():
    return 'it works!'

在您的主文件中,声明flask app的位置,导入测试路由,例如:

app.py

from flask import Flask, request, abort

app = Flask(__name__)

# import declared routes
import test-routes

它从我这边起作用。

You can use simple trick which is import flask app variable from main inside another file, like:

test-routes.py

from __main__ import app

@app.route('/test', methods=['GET'])
def test():
    return 'it works!'

and in your main files, where you declared flask app, import test-routes, like:

app.py

from flask import Flask, request, abort

app = Flask(__name__)

# import declared routes
import test-routes

It works from my side.


回答 3

将应用程序划分为多个蓝图是一个好主意。但是,如果这还不够,并且您想将蓝图本身分成多个py文件,也可以使用常规的Python模块导入系统,然后遍历从其他文件导入的所有路由来实现。

我使用以下代码创建了一个Gist:

https://gist.github.com/Jaza/61f879f577bc9d06029e

据我所知,这是目前划分蓝图的唯一可行方法。尽管存在很多与此相关的话题,但在Flask中无法创建“子蓝图”:

https://github.com/mitsuhiko/flask/issues/593

另外,即使有可能(并且可以使用该问题线程中的一些代码片段来实现),子蓝图对于您的用例也可能过于严格-例如,如果您不希望所有路线都在子模块具有相同的URL子前缀。

Dividing the app into blueprints is a great idea. However, if this isn’t enough, and if you want to then divide the Blueprint itself into multiple py files, this is also possible using the regular Python module import system, and then looping through all the routes that get imported from the other files.

I created a Gist with the code for doing this:

https://gist.github.com/Jaza/61f879f577bc9d06029e

As far as I’m aware, this is the only feasible way to divide up a Blueprint at the moment. It’s not possible to create “sub-blueprints” in Flask, although there’s an issue open with a lot of discussion about this:

https://github.com/mitsuhiko/flask/issues/593

Also, even if it were possible (and it’s probably do-able using some of the snippets from that issue thread), sub-blueprints may be too restrictive for your use case anyway – e.g. if you don’t want all the routes in a sub-module to have the same URL sub-prefix.


回答 4

使用集中式URL映射,无需蓝图和棘手的导入即可完成此任务

app.py

import views
from flask import Flask

app = Flask(__name__)

app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/other', view_func=views.other)

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

views.py

from flask import render_template

def index():
    return render_template('index.html')

def other():
    return render_template('other.html')

This task can be accomplished without blueprints and tricky imports using Centralized URL Map

app.py

import views
from flask import Flask

app = Flask(__name__)

app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/other', view_func=views.other)

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

views.py

from flask import render_template

def index():
    return render_template('index.html')

def other():
    return render_template('other.html')

获取Flask应用中定义的所有路线的列表

问题:获取Flask应用中定义的所有路线的列表

我有一个复杂的基于Flask的Web应用程序。有很多具有视图功能的单独文件。它们的URL由@app.route('/...')装饰器定义。有没有办法获取我的应用中已声明的所有路线的列表?也许有一些方法可以调用该app对象?

I have a complex Flask-based web app. There are lots of separate files with view functions. Their URLs are defined with the @app.route('/...') decorator. Is there a way to get a list of all the routes that have been declared throughout my app? Perhaps there is some method I can call on the app object?


回答 0

应用程序的所有路由都存储在app.url_map的实例上werkzeug.routing.Map。您可以Rule使用以下iter_rules方法遍历实例:

from flask import Flask, url_for

app = Flask(__name__)

def has_no_empty_params(rule):
    defaults = rule.defaults if rule.defaults is not None else ()
    arguments = rule.arguments if rule.arguments is not None else ()
    return len(defaults) >= len(arguments)


@app.route("/site-map")
def site_map():
    links = []
    for rule in app.url_map.iter_rules():
        # Filter out rules we can't navigate to in a browser
        # and rules that require parameters
        if "GET" in rule.methods and has_no_empty_params(rule):
            url = url_for(rule.endpoint, **(rule.defaults or {}))
            links.append((url, rule.endpoint))
    # links is now a list of url, endpoint tuples

有关更多信息,请参见显示指向创建的新网页的链接

All the routes for an application are stored on app.url_map which is an instance of werkzeug.routing.Map. You can iterate over the Rule instances by using the iter_rules method:

from flask import Flask, url_for

app = Flask(__name__)

def has_no_empty_params(rule):
    defaults = rule.defaults if rule.defaults is not None else ()
    arguments = rule.arguments if rule.arguments is not None else ()
    return len(defaults) >= len(arguments)


@app.route("/site-map")
def site_map():
    links = []
    for rule in app.url_map.iter_rules():
        # Filter out rules we can't navigate to in a browser
        # and rules that require parameters
        if "GET" in rule.methods and has_no_empty_params(rule):
            url = url_for(rule.endpoint, **(rule.defaults or {}))
            links.append((url, rule.endpoint))
    # links is now a list of url, endpoint tuples

See Display links to new webpages created for a bit more information.


回答 1

我刚刚遇到了同样的问题。上面的解决方案太复杂了。只需在您的项目下打开一个新的shell:

    python
    >>> from app import app
    >>> app.url_map

第一个“ app ”是我的项目脚本:app.py,另一个是我的网站名称。

(此解决方案适用于路由很少的小型网络)

I just met the same question. Those solution above is too complex. Just open a new shell under your project:

    python
    >>> from app import app
    >>> app.url_map

The first ‘app‘ is my project script: app.py, another is my web’s name.

(this solution is for tiny web with a little route)


回答 2

我在我的帮助方法manage.py

@manager.command
def list_routes():
    import urllib
    output = []
    for rule in app.url_map.iter_rules():

        options = {}
        for arg in rule.arguments:
            options[arg] = "[{0}]".format(arg)

        methods = ','.join(rule.methods)
        url = url_for(rule.endpoint, **options)
        line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
        output.append(line)

    for line in sorted(output):
        print line

它通过构建一组虚拟选项来解决缺少的参数。输出如下:

CampaignView:edit              HEAD,OPTIONS,GET     /account/[account_id]/campaigns/[campaign_id]/edit
CampaignView:get               HEAD,OPTIONS,GET     /account/[account_id]/campaign/[campaign_id]
CampaignView:new               HEAD,OPTIONS,GET     /account/[account_id]/new

然后运行它:

python manage.py list_routes

有关manage.py结帐的更多信息,请访问:http : //flask-script.readthedocs.org/en/latest/

I make a helper method on my manage.py:

@manager.command
def list_routes():
    import urllib
    output = []
    for rule in app.url_map.iter_rules():

        options = {}
        for arg in rule.arguments:
            options[arg] = "[{0}]".format(arg)

        methods = ','.join(rule.methods)
        url = url_for(rule.endpoint, **options)
        line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, url))
        output.append(line)

    for line in sorted(output):
        print line

It solves the the missing argument by building a dummy set of options. The output looks like:

CampaignView:edit              HEAD,OPTIONS,GET     /account/[account_id]/campaigns/[campaign_id]/edit
CampaignView:get               HEAD,OPTIONS,GET     /account/[account_id]/campaign/[campaign_id]
CampaignView:new               HEAD,OPTIONS,GET     /account/[account_id]/new

Then to run it:

python manage.py list_routes

For more on manage.py checkout: http://flask-script.readthedocs.org/en/latest/


回答 3

与乔纳森(Jonathan)的回答类似,我选择这样做。我看不到使用url_for的意义,因为如果您的参数不是字符串(例如float),它将中断

@manager.command
def list_routes():
    import urllib

    output = []
    for rule in app.url_map.iter_rules():
        methods = ','.join(rule.methods)
        line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, rule))
        output.append(line)

    for line in sorted(output):
        print(line)

Similar to Jonathan’s answer I opted to do this instead. I don’t see the point of using url_for as it will break if your arguments are not string e.g. float

@manager.command
def list_routes():
    import urllib

    output = []
    for rule in app.url_map.iter_rules():
        methods = ','.join(rule.methods)
        line = urllib.unquote("{:50s} {:20s} {}".format(rule.endpoint, methods, rule))
        output.append(line)

    for line in sorted(output):
        print(line)

回答 4

显然,从版本0.11开始,Flask具有内置的CLI。内置命令之一列出了路由:

FLASK_APP='my_project.app' flask routes

Apparently, since version 0.11, Flask has a built-in CLI. One of the built-in commands lists the routes:

FLASK_APP='my_project.app' flask routes

回答 5

由于您没有指定必须在命令行运行,因此可以在json中为仪表板或其他非命令行界面轻松返回以下内容。无论如何,从设计的角度来看,结果和输出确实不应该混在一起。即使是很小的程序,它也是不好的程序设计。然后,可以在Web应用程序,命令行或其他任何提取json的内容中使用以下结果。

您也没有指定您需要了解与每个路由关联的python函数,因此这可以更准确地回答您的原始问题。

我在下面使用自己将输出添加到监视仪表板。如果您想要可用的路由方法(GET,POST,PUT等),则需要将其与上述其他答案结合使用。

Rule的repr()负责在路由中转换所需的参数。

def list_routes():
    routes = []

    for rule in app.url_map.iter_rules():
        routes.append('%s' % rule)

    return routes

使用列表推导的同一件事:

def list_routes():
    return ['%s' % rule for rule in app.url_map.iter_rules()]

样本输出:

{
  "routes": [
    "/endpoint1", 
    "/nested/service/endpoint2", 
    "/favicon.ico", 
    "/static/<path:filename>"
  ]
}

Since you did not specify that it has to be run command-line, the following could easily be returned in json for a dashboard or other non-command-line interface. The result and the output really shouldn’t be commingled from a design perspective anyhow. It’s bad program design, even if it is a tiny program. The result below could then be used in a web application, command-line, or anything else that ingests json.

You also didn’t specify that you needed to know the python function associated with each route, so this more precisely answers your original question.

I use below to add the output to a monitoring dashboard myself. If you want the available route methods (GET, POST, PUT, etc.), you would need to combine it with other answers above.

Rule’s repr() takes care of converting the required arguments in the route.

def list_routes():
    routes = []

    for rule in app.url_map.iter_rules():
        routes.append('%s' % rule)

    return routes

The same thing using a list comprehension:

def list_routes():
    return ['%s' % rule for rule in app.url_map.iter_rules()]

Sample output:

{
  "routes": [
    "/endpoint1", 
    "/nested/service/endpoint2", 
    "/favicon.ico", 
    "/static/<path:filename>"
  ]
}

回答 6

如果您需要自己访问视图函数app.url_map,请使用代替app.view_functions

示例脚本:

from flask import Flask

app = Flask(__name__)

@app.route('/foo/bar')
def route1():
    pass

@app.route('/qux/baz')
def route2():
    pass

for name, func in app.view_functions.items():
    print(name)
    print(func)
    print()

运行上面脚本的输出:

static
<bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>

route1
<function route1 at 0x128f1b9d8>

route2
<function route2 at 0x128f1ba60>

(请注意,包含了“静态”路线,该路线由Flask自动创建。)

If you need to access the view functions themselves, then instead of app.url_map, use app.view_functions.

Example script:

from flask import Flask

app = Flask(__name__)

@app.route('/foo/bar')
def route1():
    pass

@app.route('/qux/baz')
def route2():
    pass

for name, func in app.view_functions.items():
    print(name)
    print(func)
    print()

Output from running the script above:

static
<bound method _PackageBoundObject.send_static_file of <Flask '__main__'>>

route1
<function route1 at 0x128f1b9d8>

route2
<function route2 at 0x128f1ba60>

(Note the inclusion of the “static” route, which is created automatically by Flask.)


回答 7

在导出或设置FLASK_APP环境变量后,可以通过运行以下命令来查看所有通过Flask Shell的路由。 flask shell app.url_map

You can view all the Routes via flask shell by running the following commands after exporting or setting FLASK_APP environment variable. flask shell app.url_map


回答 8

在烧瓶应用程序中执行以下操作:

flask shell
>>> app.url_map
Map([<Rule '/' (OPTIONS, HEAD, GET) -> helloworld>,
 <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])

inside your flask app do:

flask shell
>>> app.url_map
Map([<Rule '/' (OPTIONS, HEAD, GET) -> helloworld>,
 <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])

如何获得Flask请求网址的不同部分?

问题:如何获得Flask请求网址的不同部分?

我想检测请求是否来自localhost:5000foo.herokuapp.com主机以及请求的路径。如何获得有关Flask请求的信息?

I want to detect if the request came from the localhost:5000 or foo.herokuapp.com host and what path was requested. How do I get this information about a Flask request?


回答 0

您可以通过以下几个Request字段检查网址:

用户请求以下URL:

    http://www.example.com/myapplication/page.html?x=y

在这种情况下,上述属性的值如下:

    path             /page.html
    script_root      /myapplication
    base_url         http://www.example.com/myapplication/page.html
    url              http://www.example.com/myapplication/page.html?x=y
    url_root         http://www.example.com/myapplication/

您可以通过适当的拆分轻松提取主体部分。

You can examine the url through several Request fields:

Imagine your application is listening on the following application root:

http://www.example.com/myapplication

And a user requests the following URI:

http://www.example.com/myapplication/foo/page.html?x=y

In this case the values of the above mentioned attributes would be the following:

    path             /foo/page.html
    full_path        /foo/page.html?x=y
    script_root      /myapplication
    base_url         http://www.example.com/myapplication/foo/page.html
    url              http://www.example.com/myapplication/foo/page.html?x=y
    url_root         http://www.example.com/myapplication/

You can easily extract the host part with the appropriate splits.


回答 1

另一个例子:

请求:

curl -XGET http://127.0.0.1:5000/alert/dingding/test?x=y

然后:

request.method:              GET
request.url:                 http://127.0.0.1:5000/alert/dingding/test?x=y
request.base_url:            http://127.0.0.1:5000/alert/dingding/test
request.url_charset:         utf-8
request.url_root:            http://127.0.0.1:5000/
str(request.url_rule):       /alert/dingding/test
request.host_url:            http://127.0.0.1:5000/
request.host:                127.0.0.1:5000
request.script_root:
request.path:                /alert/dingding/test
request.full_path:           /alert/dingding/test?x=y

request.args:                ImmutableMultiDict([('x', 'y')])
request.args.get('x'):       y

another example:

request:

curl -XGET http://127.0.0.1:5000/alert/dingding/test?x=y

then:

request.method:              GET
request.url:                 http://127.0.0.1:5000/alert/dingding/test?x=y
request.base_url:            http://127.0.0.1:5000/alert/dingding/test
request.url_charset:         utf-8
request.url_root:            http://127.0.0.1:5000/
str(request.url_rule):       /alert/dingding/test
request.host_url:            http://127.0.0.1:5000/
request.host:                127.0.0.1:5000
request.script_root:
request.path:                /alert/dingding/test
request.full_path:           /alert/dingding/test?x=y

request.args:                ImmutableMultiDict([('x', 'y')])
request.args.get('x'):       y

回答 2

你应该试试:

request.url 

它假设即使在localhost上也可以一直工作(只是这样做了)。

you should try:

request.url 

It suppose to work always, even on localhost (just did it).


回答 3

如果您使用的是Python,建议您浏览请求对象:

dir(request)

由于对象支持方法dict

request.__dict__

可以打印或保存。我用它来在Flask中记录404代码:

@app.errorhandler(404)
def not_found(e):
    with open("./404.csv", "a") as f:
        f.write(f'{datetime.datetime.now()},{request.__dict__}\n')
    return send_file('static/images/Darknet-404-Page-Concept.png', mimetype='image/png')

If you are using Python, I would suggest by exploring the request object:

dir(request)

Since the object support the method dict:

request.__dict__

It can be printed or saved. I use it to log 404 codes in Flask:

@app.errorhandler(404)
def not_found(e):
    with open("./404.csv", "a") as f:
        f.write(f'{datetime.datetime.now()},{request.__dict__}\n')
    return send_file('static/images/Darknet-404-Page-Concept.png', mimetype='image/png')