Search code examples
pythonflaskurl-routingflask-restful

Two Variable Urls using Flask-Restful


This seems like something that would come up a lot, but I can't find any documentation on it.

I'm writing an api and I want urls to look like this:

'/api/v1.0/restaurant/Name&Address'

Using Flask-restful, I've defined the url as

'/api/v1.0/restaurant/<name>&<address>'

Werkzeug doesn't like this however and raises a BuildError in werkzeug/routing.py

When I define the url, with add_resource, as

'/api/v1.0/restaurant/<name>'

and hard-wire the address, everything works fine.

How do I define the urls to take two variables?

Edit

Traceback (most recent call last):
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 397, in wrapper
    resp = resource(*args, **kwargs)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 487, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/home/ubuntu/Hotsauce/api/app/views.py", line 75, in get
    resto = {'restaurant': marshal(restaurant, resto_fields)}
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 533, in marshal
    return OrderedDict(items)
  File "/usr/lib/python2.7/collections.py", line 52, in __init__
    self.__update(*args, **kwds)
  File "/home/ubuntu/.virtualenvs/data/lib/python2.7/_abcoll.py", line 547, in update
    for key, value in other:
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask_restful/__init__.py", line 532, in <genexpr>
    for k, v in fields.items())
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask_restful/fields.py", line 232, in output
    o = urlparse(url_for(self.endpoint, _external = self.absolute, **data))
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/helpers.py", line 312, in url_for
    return appctx.app.handle_url_build_error(error, endpoint, values)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/app.py", line 1641, in handle_url_build_error
    reraise(exc_type, exc_value, tb)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/flask/helpers.py", line 305, in url_for
    force_external=external)
  File "/home/ubuntu/.virtualenvs/data/local/lib/python2.7/site-packages/werkzeug/routing.py", line 1620, in build
    raise BuildError(endpoint, values, method)
BuildError: ('restaurant', {u'city_id': 2468, u’score’: Decimal('0E-10'), 'id': 37247, u'nbhd_id': 6596, u'address_region': u'NY', u'phone_number': u'(718) 858-6700', '_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x26f33d0>, u'complete': False, u'name': u'Asya', u'address_locality': u'New York', u'address_updated': True, u'street_address': u'46 Henry St'}, None)

And here is the relevant code that generates the error:

resto_fields = {
    'id': fields.Integer,
    'name': fields.String,
    'street_address': fields.String,
    'address_locality': fields.String,
    'address_region': fields.String,
    ‘score’: fields.Float,
    'phone_number': fields.String,
    'uri': fields.Url('restaurant')
    }

def get(self, name, address):
    restaurant = session.query(Restaurant).filter_by(name=name).filter_by(address=address)

    resto = {'restaurant': marshal(restaurant, resto_fields)}

    return resto

Solution

  • This has nothing to do with the & ampersand nor with with using more than one URL parameter.

    You can only use entries from the resto_fields output fields mapping in your endpoint; you don't have an address entry in your resto_fields mapping, but your restaurant endpoint requires it to build the URL.

    Add an address field to your output fields, or use one of the existing fields in the route.