I am using Flask-Restful and SQLAlchemy to develop an API and am coming across a behaviour which I have a workaround for, but I am not sure the sticking with the workaround is a good long term strategy.
Pretty standard API with a List item resource with POST function and a item single resource with GET function. I tested the POST function via curl and also via a web form, and am getting the following error message:
BuildError: Could not build url for endpoint 'brand' with values ['_sa_instance_state']. Did you forget to specify values ['brand_symbol']?
The odd thing is that adding a print statement (see commented out print statement near the end of the Resource declaration below) makes the error go away.
And I know that even when the Error message is encountered
- the item was successfully committed in the database, and
- performing a GET on the item displays the URI perfectly fine
So based on this, I think the problem is perhaps with the POST return
statement. Any help would be gratefully received - I don't want to live with a flaky workaround!
Endpoint
api.add_resource(BrandListResource, '/brands', endpoint = 'brands')
api.add_resource(BrandResource, '/brands/<string:brand_symbol>', endpoint = 'brand')
Resource
brand_fields = {
'id': fields.Integer,
'brand_symbol': fields.String,
'brand_name': fields.String,
'uri': fields.Url('brand', absolute=True)
}
class BrandResource(Resource):
@marshal_with(brand_fields)
def get(self, brand_symbol):
brand = db.session.query(Brand).filter(Brand.brand_symbol == brand_symbol).first()
if not brand:
abort(404, message="Brand {} doesn't exist".format(brand_symbol))
return brand
class BrandListResource(Resource):
@marshal_with(brand_fields)
def get(self):
brands = db.session.query(Brand).all()
return brands
@marshal_with(brand_fields)
def post(self):
parsed_args = parser.parse_args()
brand_symbol = parsed_args['brand_symbol']
brand_name = parsed_args['brand_name']
brand = db.session.query(Brand).filter(Brand.brand_symbol == brand_symbol).first()
if brand:
abort(404, message="Brand {} already exists".format(brand_symbol))
brand = Brand(brand_symbol=brand_symbol, brand_name=brand_name)
db.session.add(brand)
db.session.commit()
#print brand
return brand, 201
SQLAlchemy Model
class Brand(db.Model):
__tablename__ = 'brand'
id = db.Column(db.Integer, primary_key=True)
brand_symbol = db.Column(db.String(5), unique=True)
brand_name = db.Column(db.String(200), nullable=False)
def __init__(self, brand_symbol, brand_name):
self.brand_symbol = brand_symbol
self.brand_name = brand_name
def __repr__(self):
return '<brand_symbol {}>'.format(self.brand_symbol)
Full Error Traceback
Traceback (most recent call last):
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 2000, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1991, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 271, in error_router
return original_handler(e)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1567, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 268, in error_router
return self.handle_error(e)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1988, in wsgi_app
response = self.full_dispatch_request()
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1641, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 271, in error_router
return original_handler(e)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1544, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 268, in error_router
return self.handle_error(e)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1639, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1625, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 477, in wrapper
resp = resource(*args, **kwargs)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 587, in dispatch_request
resp = meth(*args, **kwargs)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 682, in wrapper
return marshal(data, self.fields, self.envelope), code, headers
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 640, in marshal
return OrderedDict([(envelope, OrderedDict(items))]) if envelope else OrderedDict(items)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/collections.py", line 57, in __init__
self.__update(*args, **kwds)
File "/Users/skavie/testproject/lib/python2.7/_abcoll.py", line 571, in update
for key, value in other:
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/__init__.py", line 639, in <genexpr>
for k, v in fields.items())
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask_restful/fields.py", line 307, in output
o = urlparse(url_for(endpoint, _external=self.absolute, **data))
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/helpers.py", line 332, in url_for
return appctx.app.handle_url_build_error(error, endpoint, values)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/app.py", line 1811, in handle_url_build_error
reraise(exc_type, exc_value, tb)
File "/Users/skavie/testproject/lib/python2.7/site-packages/flask/helpers.py", line 322, in url_for
force_external=external)
File "/Users/skavie/testproject/lib/python2.7/site-packages/werkzeug/routing.py", line 1758, in build
raise BuildError(endpoint, values, method, self)
BuildError: Could not build url for endpoint 'brand' with values ['_sa_instance_state']. Did you forget to specify values ['brand_symbol']?