首页 > 解决方案 > Flask:从一个路由/函数访问或传递字典到另一个

问题描述

主页是一个表单,然后当用户单击“计算”时,会从表单进行计算,存储在字典中,然后传回以填充表格。简要如下:

@app.route('/join', methods=['GET','POST'])
def my_form_post():
---calculations done and variables set---

data_dict = {
        "loco_length":l1, "loco_weight":w1, "loco_bweight":b1,
        "cargo_length":l2, "cargo_weight":w2, "cargo_bweight":b2,
        "total_length":length, "total_weight": weigth, "total_bweight": bweigth,
        "bproc":bproc
    }

    result = {str(key): value for key, value in data_dict.items()}
    return jsonify(result=result)

但是后来我做了第二个函数,我想用新的路由 /pdf 访问它。该功能是用“my_form_post”字典中的值填充预定义的 PDF 模板。

第二条路线看起来像这样,调用一个函数并需要字典作为参数:

def pdf():

    data = write_fillable_pdf(PDF_TEMP_PATH, data_dict)
    return send_file(data, mimetype='application/pdf')

如何让字典在函数和路由之间传输或访问?我见过人们提到会话、json 和 globals。但我想不通。

感谢您提供任何帮助。

标签: pythondictionaryflaskglobal

解决方案


好的,正如我所提到的,redis 是一个非常酷的后端。当您在 PythonAnywhere 上托管时,实际上有一个名为的库redislite,它将使用本地文件而不是实际的 redis 服务器来提供此功能。

看来 PythonAnywhere不支持 redis支持持久存储。更好的是,如果你以后移动到带有实际redis服务器的主机上,它应该是两行修改才能切换。

这背后的概念是:

  • /join路由中,创建一个 UUID 用作 aunique_id并将其与 dict 一起存储在 redis 中,然后将其传递unique_id给模板,将其作为链接呈现给用户。
  • /pdf路由中,根据unique_idURL字符串中的从redis获取dict。
  • 对于奖励积分,我们可以在 redis 键上设置一个过期时间,所以n几秒钟后,链接就会消失,用户必须再次点击计算按钮。

首先,您需要添加redislite要求,或pip install redislite. 然后在 Python 文件的顶部包含配置 redis 连接的必要内容:

from redislite import StrictRedis
import os
REDIS_DB_PATH = os.path.join('/tmp/my_redis.db')
r = StrictRedis(REDIS_DB_PATH, charset='utf-8', decode_responses=True)

您还应该定义到期时间,一个使 redis 键对这组路由唯一的前缀,并导入其他工具:

PDF_EXPIRY = 60*60*24 # 1 day in seconds
PREFIX = 'pdf:' # A prefix for our redis keys
from uuid import uuid4
from flask import url_for

现在下一部分工作,因为您的数据是一个简单的字典,没有嵌套。我在这里使用了一个模拟版本,然后将它添加到 redishmset并设置到期时间。

然后我将密钥添加pdf_link到您的resultdict 并将值设置为 PDF 的 URL,由 flask 的url_for函数构建:

@app.route('/join')
def my_form_post():

    # ---calculations done and variables set---

    data_dict = {
        "loco_length":3.14, "loco_weight":'str', "loco_bweight":'str',
    } # a simplified version of your dictionary

    unique_id = uuid4().__str__()

    r.hmset(PREFIX + unique_id, data_dict)
    r.expire(PREFIX + unique_id, PDF_EXPIRY)

    result = {str(key): value for key, value in data_dict.items()}

    # This creates a URL back to our PDF route, as a key in result
    result['pdf_link'] = url_for('pdf',unique_id = unique_id)  

    return jsonify(result=result)

这会产生一个 redis 哈希,其中 key:pdf:86341e77-f655-46d8-93c4-10e945fc3586和字段名称/值作为您data_dict的键/值。请参阅HMSETredis 文档以获取另一种可视化。

现在在前端result.pdf_link是一个指向 PDF 的有效链接,它看起来像:/pdf/86341e77-f655-46d8-93c4-10e945fc3586. <a href=>您可以在表格旁边的模板上的标签中呈现它。

对该 URL 的请求由下一条路由处理:

@app.route('/pdf/<string:unique_id>')
def pdf(unique_id):
    data_dict = r.hgetall(PREFIX + unique_id)
    if not data_dict:
        return 'Not found or expired', 404
    else:
        data = write_fillable_pdf(PDF_TEMP_PATH, data_dict)
        return send_file(data, mimetype='application/pdf')

我认为这种方法很干净,因为它避免了为数据构建数据库模型,而这本来是常用的方法。有了这个,您可以向 中添加更多值data_dict,只要您不嵌套它们,并且不需要更改其他任何内容。

如果您想在将来添加对真正的 redis 服务器的支持,您只需将这两行更改为:

from redis import StrictRedis
r = StrictRedis(host='newredisserver', charset='utf-8', decode_responses=True)

因为我们使用的方法在redisredislite库之间是相同的。

请注意,任何拥有 PDF 链接的人都可以下载它,前提是它尚未过期,因此您可能不应该在未经身份验证的情况下通过此链接传递个人信息


推荐阅读