I have a route that has two variable parts, and used defaults
to specify default values for each part.
@app.route("/api/<a>/<b>", defaults={"a": 0, "b": 0})
def api(a, b):
return f"{a=}, {b=}"
I want to build a URL that overrides b
while leaving a
with the default value, /api/0/
.
I tried excluding a
, url_for("api", b=1)
, but got werkzeug.routing.BuildError: Could not build url for endpoint 'api' with values ['b']. Did you forget to specify values ['a']?
. It seems that I can't exclude a variable, the default isn't substituted in.
Given that, I then tried calling with both variables, but got werkzeug.routing.BuildError: Could not build url for endpoint 'index.API.event_average_time_in_seconds' with values ['a', 'b'].
. It seems I can't override both variables either.
How can I use defaults in my route to allow not passing values for all variables?
CodePudding user response:
defaults
works differently than you're expecting. From the docs for werkzeug.routing.Rule
:
`defaults`
An optional dict with defaults for other rules with the same endpoint.
This is a bit tricky but useful if you want to have unique URLs::
url_map = Map([
Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
Rule('/all/page/<int:page>', endpoint='all_entries')
])
If a user now visits ``http://example.com/all/page/1`` they will be
redirected to ``http://example.com/all/``. If `redirect_defaults` is
disabled on the `Map` instance this will only affect the URL
generation.
You use defaults to create a default URL that does not have those corresponding variables, and it would be routed to the correct endpoint with those defaults. It will not fill in or override values for a single rule. It requires both a Rule with the defaults (and no variables), and a Rule with the variables.
Here's how you could use four rules to allow the behavior you want, overriding any combination of the variables. Typically though, you wouldn't write the default values in the URL, you'd do what the example in the docs above show and have a default rule with no values in the URL.
@app.route("/api/0/0", defaults={"a": 0, "b": 0})
@app.route("/api/<a>/0", defaults={"b": 0})
@app.route("/api/0/<b>", defaults={"a": 0})
@app.route("/api/<a>/<b>")
def api_items(a, b):
return f"{a=}, {b=}"
url_for("api_items")
/api/0/0
url_for("api_items", a=1)
/api/1/0
url_for("api_items", b=1)
/api/0/1
url_for("api_items", a=1, b=1)
/api/1/1