Python: unable to import classes from higher directory - python

I am learning flask. I have a directory structure that looks something like this.
project
- controllers
-- auth.py
-- main.py
- db
-- setup.py
- models
-- models.py
- templates
-- base.html
-- index.html
-- login.html
-- signup.html
-- 404.html
-__init__.py
The file init.py looks like this
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '9OLWxND4o83j4K4iuopO'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:#localhost/mission_to_mars'
db.init_app(app)
# blueprint for auth routes in our app
from .controllers.auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .controllers.main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
I'm trying to import the db variable and create_app() function in controllers/auth.py, controllers/main.py, models/models.py, db/setup.py
I have tried the below syntaxes
from .. import db, create_app
which gives the error:
ImportError: attempted relative import with no known parent package
I have also tried
from project import db, create_app
which gives the error
ModuleNotFoundError: No module named 'project'
I want to know how I can import classes from different directories so that I can use them further in the project.
Thanks in advance

Related

Why I can't import class declared in __init__.py?

I'm trying to clean up my flask app and I decided to use a boilerplate structure. I have the auth_bp blueprint, it's defined in auth.py , I have the following folder structure;
-app_root
-application
-__init__.py
- auth.py
- other files like template and static
- config.py (for configuration)
- server.py (to run the app through flask_script )
In __init__.py I have two classes: app and mail and I need to use them in my auth.py file.
But when I write :
from application import app, mail
I get an ImportError: cannot import name app
Here's the code in __init__.py:
from flask import Flask
from flask_mail import Mail
import config
from auth import auth_bp
app = Flask(__name__)
app.config.from_object("config.Config")
mail = Mail(app)
app.register_blueprint(auth_bp)
#app.route("/")
def home():
return "homepage"
Edit : Solved: I had to import auth after creating the app and mail object.

Python fails to import packages within same folder structure

I'm creating a personal application with flask_restful, and I was doing everything with a single models.py and app.py, but the application is going to grow so I decided to make some folder restructuring.
I currently have the following structure:
/project_folder
application_name
__init__.py
controllers.py
models.py
config.py
manage.py
run.py
tests
__init__.py
test_controller.py
Everything works so far, but I want the structure to be the following:
/project_folder
application_name
__init__.py
controllers
__init__.py
brewery_controller.py
...others
models
__init__.py
base_model.py
brewery.py
...others
config.py
manage.py
run.py
tests
__init__.py
test_controller.py
But I can't seem to make it work. Here is the application __init__.py
#/project_folder/application_name/__init__.py
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_restful import Api
from controllers import BreweryList, BreweryResource
from models import db
def initApp(config):
app = Flask(__name__)
app.config.from_object(config)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
api = Api(app)
api.add_resource(BreweryList, '/breweries')
api.add_resource(BreweryResource, '/breweries/<brewery_id>')
return app
I tried with
from brewery_controller import BreweryList, BreweryResource
from base_model import db
with no luck. I keep getting ImportError: cannot import BreweryList and the same goes for db if I uncomment the Brewery classes import line.
The controllers/__init__.py and models/__init__.py are both empty.
Here is also the run.py
import os
from beerinv import initApp
if __name__ == '__main__':
app = initApp(os.environ['APP_SETTINGS'])
app.run()
Could solve the issue by following #davidism comment by putting full import path:
from application_name.controllers.brewery_controller import BreweryList, BreweryResource
from application_name.models.base_model import db

Flask-testing - why the test does fail

I'm trying to write tests for a simple Flask application. Structure of the project is following:
app/
static/
templates/
forms.py
models.py
views.py
migrations/
config.py
manage.py
tests.py
tests.py
import unittest
from app import create_app, db
from flask import current_app
from flask.ext.testing import TestCase
class AppTestCase(TestCase):
def create_app(self):
return create_app('test_config')
def setUp(self):
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
def test_hello(self):
response = self.client.get('/')
self.assert_200(response)
app/init.py
# app/__init__.py
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
return app
app = create_app('default')
from . import views
When I launch the tests, test_hello fails because response.status_code is 404. Tell me, please, how can I fix it? It seems, that app instance doesn't know anything about view functions in the views.py. If it needs the whole code, it can be found here
Your views.py file mount the routes in the app created in your __init__.py file.
You must bind these routes to your created app in create_app test method.
I suggest you to invert the dependency. Instead the views.py import your code, you can make a init_app to be imported and called from your __init__.py or from the test file.
# views.py
def init_app(app):
app.add_url_rule('/', 'index', index)
# repeat to each route
You can do better than that, using a Blueprint.
def init_app(app):
app.register_blueprint(blueprint)
This way, your test file can just import this init_app and bind the blueprint to the test app object.

Importing Views

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

Import statement flow

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.

Categories

Resources