'How to get parameters from path in Bottle?

When i execute this url:

http://domain:8081/forum?id=2&page=26

with this code:

@route('/forum')
def display_forum():
   forum_id = request.query.id
   page = request.query.page or '1'
   return template('Forum ID: {{id}} (page {{page}})', id=forum_id, page=page)

This return on webpage Forum ID: 2 (page 26)

I need to obtain the same result calling a dynamic rest url. The url can be http://domain:8081/forum/2/26 or http://domain:8081/forum/city/place/day/hour. Don't exist a fixed number of parameters. I saw some ideas in bottle documentation, perhaps something like wildcard filter :path.



Solution 1:[1]

It won't scale to infinite routes, but something like this would work.

@route('/forum/<first>')
def test(first):
    return first

@route('/forum/<first>/<second>')
def test(first, second):
    return first, second

@route('/forum/<first>/<second>/<third>')
def test(first, second, third):
    return first, second, third

@route('/forum/<first>/<second>/<third>/<fourth>')
def test(first, second, third, fourth):
    return first, second, third, fourth

Solution 2:[2]

Use Parameters

@route ('/forum/forum_id=:forum_id&page=:page')
def display_forum(forum_id,page):
   forum_id = request.query.id
   page = request.query.page or '1'
   return template('Forum ID: {{id}} (page {{page}})', id=forum_id, page=page)

Solution 3:[3]

I would use the :path wildcard filter as you have hinted. In your example, you provided query parameters with key=value pairs and asked how to do the same using url paths but only using values with no limit of a fixed number of parameters.

/forum?id=2&page=26 /forum/2/6

Since we don't want a limit to the number of parameters, I feel that it makes sense to provide the keys in the url paths as pairs.

/forum/id/2/page/26/city/chicago/place/opera/day/monday/hour/2:20pm

Then I would parse it accordingly:

from bottle import route, run, request, template
import re

@route("/forum/<url_paths:path>", method=["GET", "POST"])
def display_forum(url_paths=None):
    # -- parse the paths as pairs of /key/value/key/value...
    match = re.compile(r"/", re.VERBOSE)
    items = map(str, match.split(url_paths))
    url_params = dict(zip(items, items))

    # -- check for any GET or POST parameters and save as dict(),
    req_params = {k:v for (k,v) in request.params.items()}

    # -- merge the url_params with req_params into one dictionary object
    params = url_params | req_params

    # -- parse forum_id and page
    if params:
        forum_id = params.pop("id") if params.get("id") else "NA"
        page = params.pop("page") if params.get("page") else "1"
        tpl = """
        Forum ID: {{id}} (page {{page}})
        % for (key, value) in params.items():
            {{key}}: {{value}}
        %end

        """
        return template(tpl, id=forum_id, page=page, params=params)
    return "please provide url paths, query params, or post data"

run(host="0.0.0.0", port=8081, reloader=True)

Sample Requests

only url paths

? curl "http://127.0.0.1:8081/forum/city/Miami/place/Dog%20Park/day/Wednesday/hour/8:20pm/id/8/page/7"

        Forum ID: 8 (page 7)
            city: Miami
            place: Dog Park
            day: Wednesday
            hour: 8:20pm

url paths and query params

? curl "http://127.0.0.1:8081/forum/city/Chicago/place/Opera%20House/day/Saturday/hour/6:20pm?id=2&page=26"

        Forum ID: 2 (page 26)
            city: Chicago
            place: Opera House
            day: Saturday
            hour: 6:20pm

url paths and POST params

? curl -X POST "http://127.0.0.1:8081/forum/event/Yoga/city/Philadelphia/place/Gym/day/Tuesday/hour/8:00am" -d "id=2&page=3"

        Forum ID: 2 (page 3)
            event: Yoga
            city: Philadelphia
            place: Gym
            day: Tuesday
            hour: 8:00am

url paths and POST form fields

? curl -X POST "http://127.0.0.1:8081/forum/event/Concert/city/Virginia%20Beach/place/GTE%20Amphitheatre/day/Friday/hour/7:20pm/price/\$65.00" -F "id=3" -F "page=16"

        Forum ID: 3 (page 16)
            event: Concert
            city: Virginia Beach
            place: GTE Amphitheatre
            day: Friday
            hour: 7:20pm
            price: $65.00

Solution 4:[4]

My solution is to combine the form values with the query values making a unified API.

def merge_dicts(*args):
    result = {}
    for dictionary in args:
        result.update(dictionary)
    return result

payload = merge_dicts(dict(request.forms), dict(request.query.decode()))
id = payload['id']
page = payload['page']

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 user1071182
Solution 2 Senthilnathan
Solution 3 Teddy Katayama
Solution 4 eatmeimadanish