I have structured my flask app in such a way that an app factory unifies all __init__.py files into one app object, like so:
app.py
tasks.py
/project/
__init__.py
routes/
__init__.py
auth_bp.py
register_bp.py
models/
__init__.py
user.py
base.py
app.py
from project import create_app
app = create_app()
project/__init__.py
from flask import Flask
def create_app():
from . import routes, models
app = Flask(__name__)
models.init_app(app)
routes.init_app(app)
return app
project/models/__init__,py
from base import db
def init_app(app):
db.init_app(app)
project/routes/__init__.py
from auth import auth_bp
from register import register_bp
def init_app(app):
app.register_blueprint(auth_bp)
app.register_blueprint(register_bp)
what I'm trying to wrap my head around is how to structure some background processes which depend on app.context(), like so:
project/tasks.py
import sys
sys.path.append('/path/to/root')
from app import app
def load_cache(track_and_features):
while True:
with app.app_context():
Upload_Track(track_and_features)
#app.route('/cache')
def cache(track_and_features):
executor.submit(load_cache, track_and_features)
return 'Ok'
the problem (I guess) occurs here, when I call cache(), in a module which imports the app object on its turn:
project/routes/auth_bp.py
from tasks import cache
#auth_bp.route("/callback/q")
def callback():
(...)
cache()
return redirect(url_for('register.homepage'))
I am getting the error:
File "app.py", line 6, in <module>
app = create_app()
File "/Users/me/Documents/Code/Apps/Production/project/__init__.py", line 20, in create_app
from . import routes
File "/Users/me/Documents/Code/Apps/Production/project/routes/__init__.py", line 1, in <module>
from auth import auth_bp
File "/Users/me/Documents/Code/Apps/Production/project/routes/auth.py", line 18, in <module>
from tasks import cache
File "/Users/me/Documents/Code/Apps/Production/tasks.py", line 4, in <module>
from app import app
File "/Users/me/Documents/Code/Apps/Production/app.py", line 6, in <module>
app = create_app()
File "/Users/me/Documents/Code/Apps/Production/project/__init__.py", line 20, in create_app
from . import models, routes, services
ImportError: cannot import name routes
How do I fix this?
Don't import app in routes. With an app factory, you do not have an app to register routes with, outside of the factory. Instead, use blueprints for all your views, and import the blueprint into the factory. You could still register your views with the app object inside the factory, but that's just not nearly as clean as just using a blueprint to register views with.
You already do this with dedicated init_app() functions, but your project/routes/tasks.py module doesn't follow this pattern. It too should be using a blueprint. You can share a blueprint between modules, if that simplifies your application.
The only point you actually create an app name, is to run the whole app, so for the WSGI server, tests or command-line tools.
Related
I've rummaged through maybe 50 different answers to this and still I haven't manage to fix it... I'm pretty new to flask and python.
I have an app which was running great locally but I've struggled with deploying on python anywhere. Initially I had a few import modules issues, now it runs but doesn't return any html template, despite not seeing any other issue. The main issue I had was that it couldn't find the "routes" app from wsgi, and I sort of fixed adding the app = Flask(name) line on routes.py (the short blueprint object is not callable).
routes.py:
from flask import Blueprint, render_template, request, redirect, send_file
import pyqrcode
from pyqrcode import QRCode
import subprocess
from extensions import db
from models import Link
app = Flask(__name__)
short = Blueprint('short', __name__, url_prefix='/')
#short.route('/index')
def index():
return render_template('index.html')
init.py
from flask import Flask
from extensions import db
from routes import short
def create_app(config_file='settings.py'):
app = Flask(__name__)
app.config.from_pyfile(config_file)
db.init_app(app)
app.register_blueprint(short)
return app
wsgi.py
import sys
# add your project directory to the sys.path
project_home = u'/home/b297py/mysite'
if project_home not in sys.path:
sys.path = [project_home] + sys.path
# import flask app but need to call it "application" for WSGI to work
from routes import app as application
For the purpose of testing, I've placed all the html templates both in the root directory and in the specific /template directory but it just didn't fix the issue.
In wsgi.py you are not calling create_app so you are never registering the blueprint. You should replace :
from routes import app as application
by something like :
from package_name import create_app
application = create_app()
(Example: https://www.pythonanywhere.com/forums/topic/12889/#id_post_50171)
Also as you mentioned, the fix adding app = Flask(__name__) to routes.pyallows you to bypass create_app (so you should remove it if you want to stick to the create_app approach).
I am using Flask for my web framework. I am having an issue with imports. I am not understanding why can't I import my variable when I declare it within my my_app/__init__.py:
from flask import Flask
from flask_login import LoginManager
from my_app.some_module.my_class.py import auth
app = Flask(__name__)
login_manager = LoginManager()
class Config:
def __init__(self):
pass
config = Config()
My conflictuous imports are present in my_app/some_module/my_class.py:
from flask import Blueprint
from my_app import login_manager # this one works fine
from my_app import config
auth = Blueprint('auth', __name__)
I run the app with run.py:
from my_app import app
app.run(debug=True)
I then get the error:
Traceback (most recent call last):
...
File ".../my_app/some_module/my_class.py", line 1, in <module>
from my_app import login_manager, config
ImportError: cannot import name 'config' from 'my_app' (.../my_app/__init__.py)
Project structure is:
my_app
+ __init__.py
some_module
+ __init__.py
+ my_class.py
+ run.py
You have a cyclic import: my_app.some_module -> my_app.some_module.my_class -> my_app.some_module.
You can fix this by moving both Config and config to a separate module my_app.some_module.config.
# my_app.some_module.my_config
class Config:
pass
config = Config()
# my_app.some_module.my_class
from .my_config import config
# my_app.some_module.__init__
from .my_config import config
from .my_class import MyClass
This means that every import does not depend on previous imports:
my_app.some_module
|-> my_app.some_module.my_class -> my_app.some_module.config
\-> my_app.some_module.my_config
Doing imports this way instead of moving the import for .my_class to the end of __init__.py is more robust. You can freely reorder the imports of .my_class and .my_config at the top of files.
The problem is that you have a cyclic dependency. By the time you import auth from my_app.some_module.my_class.py your config is not set yet. Try moving that import to the end of the my_app/__init__.py file like:
from flask import Flask
from flask_login import LoginManager
app = Flask(__name__)
login_manager = LoginManager()
class Config:
pass
config = Config()
from my_app.some_module.my_class.py import auth
I'm trying to passing the flask app context with some global variable to the nested api context.
run.py <= Start the flask application via app.run()
app/
__init__.py
api/
__init__.py
controllers.py
app/__init__.py
from flask import Flask
from app.api.controllers import api
app = Flask(__name__)
app.register_blueprint(api, url_prefix='/api')
main_dict = {"a":"info1", "b":"info2}
app/api/controllers.py
from flask import Blueprint, jsonify
from app import app, main_dict
api = Blueprint('api', __name__)
#api.route('/')
def test():
return jsonify({"status":"OK"})
It throws the following error message:
ImportError: cannot import name 'app'
Thanks!
You've created a circular dependency.
app wants to import app.api.controllers, but that wants to import from app. What you should do to fix it, is to create a new module containing main_dict and import that in both app and app.api.controllers:
# app/__init__.py
from .main_dict import main_dict
from .api.controllers import api
# app/main_dict.py
main_dict = {'a': 'A'}
# app/api/__init__.py
# app/api/controllers.py
from flask import current_app as app
from ..main_dict import main_dict
if you need access to the app outside of app/__init__.py, the recommended way to get at it is to use from flask import current_app
My app layout
my_app
__init__.py
my_app
__init__.py
startup
create_app.py
create_users.py
common_settings.py
core
models.py
views.py
Inner __init__.py
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__) # The WSGI compliant web application object
db = SQLAlchemy(app) # Setup Flask-SQLAlchemy
manager = Manager(app) # Setup Flask-Script
from my_app.startup.create_app import create_app
create_app()
create_app.py
from native_linguist_server import app, db
#app.before_first_request
def initialize_app_on_first_request():
""" Create users and roles tables on first HTTP request """
from .create_users import create_users
create_users()
def create_app(extra_config_settings={}):
app.config.from_envvar('ENV_SETTINGS_FILE')
# Load all blueprints with their manager commands, models and views
from my_app import core
return app
When I run my app like this and try to load a view in my browser, I get a 404 error.
However if I change:
from my_app import core
to
from my_app.core import views
it works fine.
Could someone please explain to me the difference between these two calls? I would have thought from my_app import core would also import views.py and hence there wouldn't be an issue.
Thank you.
from my_app import core
will load and execute my_app/core/__init__.py (if it exists). You will then have access to any identifiers defined inside or imported into __init__.py.
from my_app.core import views
will load and execute my_app/core/views.py. You'll then have access to any identifiers defined inside or imported into views.py.
To get the behavior you're expecting, you'll need to import views inside __init__.py:
from . import views
My app layout
my_app
__init__.py
my_app
__init__.py
startup
__init__.py
create_app.py
create_users.py
common_settings.py
core
__init__.py
models.py
views.py
errors
__init__.py
errors.py
Inner __init__.py
from flask import Flask
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__) # The WSGI compliant web application object
db = SQLAlchemy(app) # Setup Flask-SQLAlchemy
manager = Manager(app) # Setup Flask-Script
from my_app.startup.create_app import create_app
create_app()
create_app.py
def create_app(extra_config_settings={}):
# Load all blueprints with their manager commands, models and views
from my_app import core
return app
core/__init__.py
# from . import views
views.py
from my_app import app, db
from flask import Flask, request
#app.errorhandler(Error)
def handle_invalid_usage(error):
response = jsonify(data=error.to_dict())
response.status_code = error.status_code
return response
I based this code on a tutorial I found. Everything works fine as long as I leave the __init__.py in the core folder empty.
When I don't, I get a NameError: name Error is not defined in my views.py. Error comes from errors.py.
I have three questions:
1) Why does this happen only when I leave the import statement in core/__init__.py.
2)
create_app.py
app.config.from_envvar('ENV_SETTINGS_FILE')
# Other app.config commands here
from my_app import core
return app
What happens when from my_app import core runs?
3) Finally when I return app, is this to ensure that Inner __init__.py file contains the updated app object?
Any explanations would be greatly appreciated!
Trying to build and configure an app with dynamic imports is really bad news and confusing for the reasons you are discovering. A much better and understandable pattern would be a more typical factory:
def create_app():
app = Flask(__name__)
configure_app(app, config)
register_db(app)
add_views(app)
add_manager(app)
return app
if __name__ == '__main__':
app = create_app()
app.run()
But since you're asking, your problem is here:
from my_app import app, db
from flask import Flask, request
#app.errorhandler(Error) # Error is not imported
def handle_invalid_usage(error):
response = jsonify(data=error.to_dict())
response.status_code = error.status_code
return response
The error occurs because views.py is imported, the code compiler comes across Error and cannot find a reference to it.
For your second question: from my_app import core causes core.__init.__ to run, which (presumably) adds the views onto the app object.