'Dynamically generate Flask routes

I am trying to dynamically generate routes in Flask from a list. I want to dynamically generate view functions and endpoints and add them with add_url_rule.

This is what I am trying to do but I get a "mapping overwrite" error:

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")


Solution 1:[1]

You have one problem with two possible solutions. Either:

  1. Have route[func] directly reference a function, not a string. In this case, you don't have to assign anything to app.view_functions.

Or:

  1. Leave out the third argument of app.add_url_rule, and assign a function to app.view_functions[route["page"]]. The code

    return render_template("index.html")
    

    is not a function. Try something like

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

I'd recommend the first option.

Source: the docs.


Alternate solution:

Use variable parts in the URL. Something like this:

@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)

Solution 2:[2]

Not too familiar with Flask, so it is possible that there is a cleaner way to do this. (If someone who is knowledgeable about Flask thinks that my method is inherently wrong, I'll gladly delete my answer if they explain why in a comment.) Now that I got that disclaimer out of the way, here are my thoughts:

app.route("/") is a decorator function. The @ notation is just syntactic sugar for something like index = app.route("/")(index). Therefore, you should be able to do something like this...

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

which would allow you to create the Flask routes from dynamically created routes and functions.

Solution 3:[3]

This is how i got it to work @this-vidor and @PZP, the get page method is querying an sqlite db (but it could be any db), the generic function def is being looped over and in my actual code list of dictionaries is also being pulled from a db. So basically what I accomplished what I needed. The routes are dynamic. I can turn the routes on and off in the sql with out having to go to app.py to edit them.

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`

Solution 4:[4]

Not directly replying to the OP but, as it could be useful to others, here is a tested example to dynamically create a POST route for multiple functions, each function taking a variable number of parameters and all returning a dict :

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"])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 pzp
Solution 3 Jesse
Solution 4