David Carneiro
David Carneiro

Reputation: 43

Flask - Routing with multiple optional parameters

I have a Route named search: @app.route('/search') Is it possible to add multiple optional parameters to it? Example:

@app.route('/search/pg/<int:pg>')
@app.route('/search/types/<types>')
@app.route('/search/number/<int:number>')
@app.route('/search/subject/<subject>')

The order in the URL shouldnt matter, so I could call /search/pg/2, or /search/subject/MySubject/pg/2 or /search/types/posts/subject/MySubject/pg/2

I tried this, but it only works with the full paths and all the parameters:

@app.route('/search/pg/<int:pg>/types/<types>/subject/<subject>', methods=['GET', 'POST'])
@app.route('/search/subject', defaults={'subject', None})
@app.route('/search/pg/<int:pg>/types/<types>', methods=['GET', 'POST'])
@app.route('/search/types', defaults={'types', None})
@app.route('/search', defaults={'pg': 1}, methods=['GET', 'POST'])
@app.route('/search/pg/<int:pg>', methods=['GET', 'POST'])
def search(pg, types=None, subject=None):
    pass

Upvotes: 4

Views: 16472

Answers (3)

deesolie
deesolie

Reputation: 1062

If you are trying to route a user based on multiple, optional form values as route parameters, then I found a useful workaround.

First, create an intermediate route that will create the query string. This route will only allow for POST methods (since the 1 or more of the form values would be submitted by the form).

@app.route('/example', methods=['POST']
def createQueryParams():
  form = ExampleForm()
  # Retrieve/cleanse/validate form data
  example1 = form.example_field1.data
  example2 = form.example_field2.data
  return redirect(url_for('app.route', example1=example1, example2=example2))  

Note that each keyword argument in the redirect(url_for()) needs to either be a parameter used in app.route or something you expect to add as a query parameter.

Next, alter your app.route() to have a GET method and extract the query parameters like @lee-pai-long mentioned

@app.route('/search', methods=['GET'])
def search():
  if len(request.args) > 0:
    example1 = request.args.get('example1')
    example2 = request.args.get('example2')
    # Do stuff

Finally, in the .html template where the form is submitted, make sure the form action directs to the createQueryParams route, not the search route. Such as

<form action="{{ url_for('app.createQueryParams') }}" method="POST">
  <!-- Flask form stuff -->
</form>

I used this structure to create a page where users could filter posts based on up to X different criteria (title search, date sort, tags, etc.). I wanted to be able to have an optimized query parameter, but without the intermediary createQueryParams route, I was not able to determine what form values were selected until after the url was created, since the single route owned both methods.

Upvotes: 2

sudouser2010
sudouser2010

Reputation: 171

@David, I worked on a package that does this called flask_optional_routes. The code is located at: https://github.com/sudouser2010/flask_optional_routes.

from flask import Flask

from flask_optional_routes import OptionalRoutes


app = Flask(__name__)
optional = OptionalRoutes(app)

@optional.routes('/<user_id>/<user_name>?/')
def foobar(user_id, user_name=None):
    return 'it worked!'

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=5000)

Upvotes: 2

lee-pai-long
lee-pai-long

Reputation: 2292

You can use filter in the URL instead of "sub-resources". Then you can put search arguments in any order in your request: /search?pg=<pg>&types=<types>

Inside the flask view function you can retrieve parameters from the request object:

@app.route('/search/')
def search():
    pg = request.args.get('pg')
    ...

Upvotes: 11

Related Questions