动态生成Flask路由

13

我正在尝试从列表中动态生成Flask的路由。我想要动态生成视图函数和端点,并使用add_url_rule添加它们。

这是我尝试做的事情,但我遇到了“mapping overwrite”错误:

routes = [
    dict(route="/", func="index", page="index"),
    dict(route="/about", func="about", page="about")
]

for route in routes:
    app.add_url_rule(
        route["route"], #I believe this is the actual url
        route["page"], # this is the name used for url_for (from the docs)
        route["func"]
    )
    app.view_functions[route["func"]] = return render_template("index.html")
4个回答

15

你有一个问题,有两种可能的解决方案:

  1. route[func]直接引用一个函数而不是一个字符串。在这种情况下,您不必将任何东西分配给app.view_functions

或者:

  1. 省略app.add_url_rule的第三个参数,并将一个函数分配给app.view_functions[route["page"]]。代码如下:

     return render_template("index.html")
    

不是一个函数。尝试使用类似于以下的内容:

    def my_func():
        return render_template("index.html")
    # ...
    app.view_functions[route["page"]] = my_func

我建议选择第一个选项。

来源:文档


另一种解决方案:

在URL中使用变量部分。像这样:

@app.route('/<page>')
def index(page):
  if page=='about':
     return render_template('about.html') # for example
  else:
     some_value = do_something_with_page(page) # for example
     return render_template('index.html', my_param=some_value)

好的,我会尝试翻译。 - Jesse
1
@Jesse 你剩下的问题是否与你最初的问题有关?不清楚你所描述的Jinja2模板如何与我最近的追问有关。 - A. Vidor
基本上我想要的是让路由调用一个通用函数,路由中的参数 "page" 是为了从 SQLite 或其他数据库中获取页面数据,所以生成页面所需的所有信息都在路由列表中,所以流程将是:调用路由,发送 "page" 参数到数据库,返回页面的部分并呈现。因此,如果我有100个页面或2个页面,路由是动态的,只需要将页面参数传递给数据库即可。这就是为什么我需要一个通用函数,以便我可以添加和删除页面而不必手动编辑路由的原因。 - Jesse
更好的解决方案是使用路由中的变量部分。采用这种方法,您将定义一个端点,例如app.route('/<page>')。装饰的视图函数将接受一个名为page的参数,并根据该参数委派其他操作。我会更新我的答案,包括这个解决方案。 - A. Vidor
实际上我尝试过了。我在扩展路由时遇到了问题。例如,如果我要为其编写REST API,则设置app.route(“/<page>”)将使页面通用化,并且您可以传递所需的变量并进行查询。但是,您是否无法添加其他参数?例如,app.route(“/<page>/<item>”)? - Jesse
显示剩余4条评论

7

我对Flask不是很熟悉,所以可能有更简洁的方法来解决这个问题。(如果了解Flask的人认为我的方法本质上是错误的,如果他们在评论中解释原因,我将乐意删除我的答案。)既然我已经说明了免责声明,以下是我的想法:

app.route("/")是一个装饰器函数。@符号只是一种语法糖,类似于index = app.route("/")(index)。因此,您应该能够像这样做...

routes = [
    ("/", index),
    ("/about", about)
]
for route, view_func in routes:
    view_func = app.route(route)(view_func)

这将允许您从动态创建的路由和函数中创建Flask路由。


好的,我明白了。我不知道你可以用那种方式做。我会尝试一下。 - Jesse
@pzp 这个解决方案本质上没有任何问题,只是与 Flask.add_url_rule 方法并没有什么不同。(Flask.route 装饰器在内部使用了 Flask.add_url_rule) 但是你的代码通过将 view_func 绑定到一个函数而不是一个字符串来解决了 OP 的问题。 - A. Vidor
@this-vidor 好的。我刚刚阅读了add_url_rule的文档,你是正确的。我已经给了你一个赞,但为了完整起见,我也会在这里留下我的答案。 - pzp
@pzp 谢谢!我并不是出于竞争的心态在评论,而是为了解决你对自己的方法是否“本质上错误”的不确定性。它在语法上非常优雅。 - A. Vidor
小修正 - 最好添加方法信息:self.route(route, methods = route_methods)(func) - Vashu
如何使用您的方法添加请求变量。例如,在Flask中通过ID获取用户时,可以使用@app.route("/user/<id>");;;; def get_user(id): #SOME LOGIC... 如何使用您的方法实现这一点。 - undefined

2

虽然这不是直接回复楼主的问题,但对于其他人可能会有用。下面是一个经过测试的示例,可以动态创建多个函数的POST路由,每个函数都接受可变数量的参数并返回一个字典:

def dummy_func_1(x, y):
    return {"a":0, "b":1}

def dummy_func_2(x):
    return {"a":2}

FUNCTIONS_ROUTES = [dummy_func_1, dummy_func_2]

def create_view_func(func):
    def view_func():
        dict_input = request.get_json()
        dict_output = func(**dict_input)
        return flask.jsonify(dict_output)
    return view_func

for func in FUNCTIONS_ROUTES:
    app.add_url_rule(rule=f"/{func.__name__}",
                     endpoint=func.__name__,
                     view_func=create_view_func(func),
                     methods=["POST"])

0
这是我如何让它在@this-vidor和@PZP上工作的,get页面方法正在查询sqlite数据库(但它可以是任何数据库),通用函数def正在循环执行,并且在我的实际代码中,字典列表也从数据库中提取。所以基本上我完成了我需要的功能。路由是动态的。我可以在sql中打开和关闭路由,而无需转到app.py进行编辑。
defaultPage = "/"

@app.route(defaultPage)
def index():
    page = getPage(defaultPage)
    return render_template("index.html", page=page)



routes = [
    dict(route="/", func="index", page="index"),
    dict(route="/about", func="about", page="about")
]

def generic():
    rule = request.url_rule
    page = getPage(rule)
    return render_template('index.html', page=page)

for route in routes:
    app.add_url_rule(
        route["route"], #I believe this is the actual url
        route["page"] # this is the name used for url_for (from the docs)
    )
    app.view_functions[route["func"]] = generic`

1
很高兴你把它搞定了!请记住,为了使其正常工作,route["func"] 必须始终等于 route["page"](那么为什么要分别定义它们呢?)。这真的不会引发异常吗?因为顶部的代码定义了 "/" 端点的路由,循环的第一次迭代也是如此。此外,使用变量部分(如我的答案中所述)而不是在 generic 函数中检索/解析 request.url_rule 可能更加清晰。 - A. Vidor
特别是因为这样做将彻底消除对多个规则定义的需求。 - A. Vidor
@this-vidor - 我将尝试您的方法并使用app.route("/<page>")进行测试。我认为这也是一个好方法。如果我在尝试做什么方面不清楚,我很抱歉,我习惯于使用Java、Coldfusion、C#和node.js。我认为其中一些方法在Python中不太适用。我意识到您关于使用app.route("/page")的建议更有意义,因为您只使用了一个函数,而循环实际上正在为每个路由生成一个函数。 - Jesse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接