Code as follows (ignore random variable names):
from colombia.views import CatAPI
api.add_resource(CatAPI, "/cats/<int:cat_id>")
api.init_app(app)
gives me:
Traceback (most recent call last):
File "/Users/makmana/colombia/env/lib/python3.4/site-packages/flask_restful/__init__.py", line 112, in init_app
app.record(self._deferred_blueprint_init)
AttributeError: 'Flask' object has no attribute 'record'
When I check the code in this repo, it looks like that exception should have been catched:
If I initialize the app as:
api.blueprint = app
instead of calling init_app, it seems to work fine. At first I thought maybe the code I have is old, but then I checked the versions and reinstalled packages:
Flask-RESTful==0.3.0
Flask==0.10.1
There's definitely something funky going on here - especially since I'm not using blueprints - but I'm not quite sure what. It also seems to happen only when I trigger it through test cases through flask-testing. Hope this helps.
Since you only see the behavior when testing, It may be helpful if you can provide an example of a test case where this exception surfaces.
Actually, I think this error was a red herring - it was being raised while another exception was being raised - probably because the exception handling code expected init_app to have run completely, but it hadn't yet. The other exception was related to duplicate routes (AssertionError: View function mapping is overwriting an existing endpoint function) - turns out that the flask-restful Api() was a global, and when the test case was being torn down, the state in Api() remained the same, so when it was being set up again in a new test case, the route was already there. The solution was to create a class similar to this (untested code):
class ext(object):
db = SQLAlchemy()
api = restful.Api()
@classmethod
def reset(cls):
cls.db = SQLAlchemy()
cls.api = restful.Api()
And call reset() on teardown etc. An alternative would be to associate the extensions with the current app object if you need to avoid the convenience of global state completely. There might be a neater solution here to do with the flask application context - but that's a story for another github issue.
Thanks!
I'm experiencing the same problem. It's telling me that I'm overwriting an existing endpoint on my second test. I'm not sure how to implement the workaround mentioned above. My current tearDown method is:
def tearDown(self):
super(MyAppTestCase, self).tearDown()
db.drop_all()
self.app_context.pop()
The answer was simple. I moved api = Api(...) into my create_app factory instead of keeping it as a global.
I realize that this issue was closed but I'm running into this issue. Basically the init_app() doesn't work in testing and not using it isn't a solution, it's a work around. The fact that it doesn't work in testing makes me think that it won't work in production either and I don't want to be dealing with bugs.
Does anyone have insight on how to get init_app to work?
For what it's worth, here is how I got this working inside of an app factory and bypassing init_app() issue. I basically monkey patch the api.app attribute with the current app. When I patch it the api instance isn't attached to any flask instance yet.
Inside extensions.py
from flask_restful import Api
api = Api()
Inside app.py
```python
from extensions import api
from settings import ProductionConfig
def create_app(config=ProductionConfig):
""" flask application factory """
app = Flask(__name__)
# attach the api routes
register_api(app)
def register_api(app):
""" attaches api routes to the flask app """
api.app = app
api.add_resource(FirstResource, '/api/first_resource')
api.add_resource(SecondResource, '/api/second_resource')
api.add_resource(ThirdResource, '/api/third_resource')
"""
Although the issue was closed, I also run into the issue.Here is how I deal with it in my unit test:
from app import create_app, db, api, admin
class TestURLS(unittest.TestCase):
def setUp(self):
# Bug workarounds
admin._views = []
api.resources = []
app = create_app('test')
self.client = app.test_client()
# Bug workarounds
db.app = app
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
As it shown above, api.resources=[] is the point.And if you use flask_admin, you shall run into the same issue as well as flask_restful.Of course, the similar solution is admin._views=[].
I ran into this issue as well, and solved it by introducing a static variable in my create_app function:
def create_app():
# ...
if not (create_app.resources_added):
# register api resources
for path, resourceObj in resources.resources_dict.items():
api.add_resource(resourceObj, path)
create_app.resources_added = True
# initialize api
api.init_app(app)
return app
# initialize creat_app.resources_added static variable
create_app.resources_added = False
Below is the entire file (app/__init__.py) for reference:
from flask import Flask
from config import app_config
from flask_restful import Api
from flask_cors import CORS
from app.database import Database
api = Api()
cors = CORS()
db = Database()
def create_app(config_name: str) -> Flask:
# imports
from app import resources
# configure app
app = Flask(__name__)
app.config.from_object(app_config[config_name])
with app.app_context():
# initialize database
db.init_app(app)
# register cors
cors.init_app(app, resources={r"*": {"origins": "*"}})
# FlaskRestful does not integrate nicely with Flask app_context functionality.
# Since its useful for testing purposes to be able to create multiple apps within one testing suite
# we are using a static variable to check whether the resources have been added to the API in this process.
# This stops attempting to register the resources twice with the API object, which causes an error.
if not (create_app.resources_added):
# register api resources
for path, resourceObj in resources.resources_dict.items():
api.add_resource(resourceObj, path)
create_app.resources_added = True
# initialize api
api.init_app(app)
return app
# initialize creat_app.resources_added static variable
create_app.resources_added = False
Most helpful comment
For what it's worth, here is how I got this working inside of an app factory and bypassing
init_app()issue. I basically monkey patch theapi.appattribute with the current app. When I patch it theapiinstance isn't attached to any flask instance yet.Inside
extensions.pyInside
app.py```python
from extensions import api
from settings import ProductionConfig
def create_app(config=ProductionConfig):
""" flask application factory """
app = Flask(__name__)
register_api(app)
def register_api(app):
""" attaches api routes to the flask app """
api.app = app
api.add_resource(FirstResource, '/api/first_resource')
api.add_resource(SecondResource, '/api/second_resource')
api.add_resource(ThirdResource, '/api/third_resource')
"""