0. 缘起
最近用 flask 写了一个 web 应用,需要部署到服务器上。而服务器主域名已经被使用了,只能给主域名加个子目录进行部署,比如主域名 example.org
,我需要在 example.org/flask
下部署。这时 flask 应用里的内部连接们就出现问题了,因为 flask 应用默认都是部署在根目录,比如项目的 css 定义在 /static/sytle.css
这个地址下,而部署到子目录 /flask
后,浏览器会去 /flask/static/style.css
找这个文件。怎么办?
查看源代码,发现 flask 的内部链接大都使用 url_for
函数实现的,那么解决方案就来了:修改这个函数,让它固定返回一个前缀。
1. 覆写 url_for()
函数测试一下
在 flask 项目目录下,新建一个 common.py
文件,内容如下:
from flask import current_app
def url_for(endpoint, **values):
'''override flask.url_for() to add a prefix to the url'''
return '/flask' + current_app.url_for(endpoint, **values)
然后到主程序(名叫 myapp.py)里面使用它,代替原来的 flask.url_for
,测试代码如下:
from flask import Flask
from .common import url_for
app = Flask(__name__)
@app.route("/hello")
def hello_world():
return f"地址: {url_for('hello')}"
现在将程序跑起来看看,在命令行下输入:
flask --app myapp run --debug
在浏览器端访问地址 http://127.0.0.1:5000/hello
,我们可以看到如下结果:
好了,第一步大功告成!
现在,我们只需要在各个模块中,将原先的 from flask import url_for
语句,统统替代为 from .common import url_for
,就🆗了。
2. 在模板中使用新的 url_for
flask v3 默认使用 jinja2 模板,在其中可以直接使用 url_for 函数,来实现 url 相关的代码解耦。但是前述自定义 url_for
要如何才能在模板中生效呢?
经查询 flask 的官方文档,找到了一个修饰器 context_processor,说是在 jinja2 模板渲染前(即 render_template()
函数运行前),注册好自定义的模板处理函数。这个文档,说实在没看太明白。索性打开源代码看看。
用 vscode 打开项目虚拟环境中的源代码:
code venv\Lib\site-packages\flask
查找 context_processor
,发现是在 def update_template_context(self, context: dict[str, t.Any]) -> None:
这个函数定义中处理 context_processor
。但是这个函数体咱也看不懂呀,只好问问 kimi 了,结果人家果然将函数解析得明明白白。
![向 kimi 问函数语法](https://i-blog.csdnimg.cn/direct/22c3612787d640cfb80f079681a252d7.png#pic_center
最后我让 kimi 给我一个示例,这下就真的懂了。因此打开我的 myapp.py
,加入如下代码:
@app.context_processor # 这里是将自定义的 url_for 函数传递给 jinja2 模板
def inject_custom_url_for(): # 这个函数名不重要,任意均可
return dict(url_for=url_for)
并修改 def hello_world()
,加入模板测试一下:
@app.route("/hello")
def hello_world():
from flask import render_template_string
return f"""本页地址: {url_for('hello')}
<br> 模板: {render_template_string("{{ url_for('static', filename='style.css') }}")}"""
运行起来之后,就可以看到模板内的地址果然也加上了前缀了。
3. 将前缀加入到配置中
现在,我们需要将前面硬编码的前缀,改为可以在部署之后随意修改的配置文件,以适应实际的部署场景。配置文件采用 flask 中最常见的 config.py
。修改 common.py
如下:
from flask import current_app
def url_for(endpoint, **values):
'''override flask.url_for() to add a prefix to the url'''
prefix = current_app.config.get('URL_PREFIX', '')
return prefix + current_app.url_for(endpoint, **values)
现在,我们随时可以修改部署在服务器端的 config.py
文件,加入如下配置,即可灵活修改网页服务器的子目录前缀了:
URL_PREFIX=/flask
至此,大功告成!
参考
- Masked5,02_详解Flask中的URL ——url_for() 与 自定义动态路由过滤器: https://blog.csdn.net/Drifter_Galaxy/article/details/116106315
- flask 之 url_for() 函数解析: https://blog.csdn.net/lovedingd/article/details/106671247
- flask 3.0.x 官方文档: https://flask.palletsprojects.com/en/3.0.x/api/