I'm not able to create tables into a database (PostgresSQL) when using Flask's app factory pattern.
I've looked at different examples from Stackoverflow and the Flask-SQLAlchemy source code. My understanding is that with the factory app pattern, I need to set up the so-called context before I can try creating the tables. However, when I create the context, Flask app's config dictionary gets reset and it doesn't propagate the configurations forward.
Here's my model.py
import datetime
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class MyModel(db.Model):
id = db.Column(db.Integer, primary_key=True)
some = db.Column(db.DateTime, nullable=False)
random = db.Column(db.String, nullable=False)
model = db.Column(db.String, nullable=False)
fields = db.Column(db.Integer, nullable=False)
def __init__(self, some: datetime.datetime, random: str, model: str,
fields: int) -> None:
self.some = some
self.random = random
self.model = model
self.fields = fields
def __repr__(self):
return f"""<MyModel(some={self.some}, random={self.random},
model={self.model}, fields={self.fields})>"""
Here's the app's __init__.py file
import os
from flask import Flask
from flask_migrate import Migrate
from myapp.models import db
migrate = Migrate()
def create_app(config):
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
migrate.init_app(app, db)
from .models import MyModel
with app.app_context():
db.create_all()
#app.route('/hello')
def hello():
return('Hello World!')
return app
I also have a main.py file:
from myapp import create_app
from config import Config
app = create_app(Config)
if __name__ == "__main__":
app.run(host='127.0.0.1', port=8080, debug=True)
The root folder contains the main.py and the MyModule app folder. Furthermore, I've set up a Postgres instance and the required config constants in a config.py file:
import os
from dotenv import load_dotenv
basedir = os.path.abspath(__file__)
load_dotenv(os.path.join(basedir, '.env'))
class Config(object):
DEBUG = False
TESTING = False
CSRF_ENABLED = True
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
SQLALCHEMY_TRACK_MODIFICATIONS = False
I'm reading the variables from an .env file.
When I run main.py, I get the following error:
(venv) $:project-name username$ python main.py
Traceback (most recent call last):
File "main.py", line 4, in <module>
app = create_app(Config)
File "/Users/username/project-name/myapp/__init__.py", line 18, in create_app
db.create_all()
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 1033, in create_all
self._execute_for_all_tables(app, bind, 'create_all')
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 1025, in _execute_for_all_tables
op(bind=self.get_engine(app, bind), **extra)
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 956, in get_engine
return connector.get_engine()
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 560, in get_engine
options = self.get_options(sa_url, echo)
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 575, in get_options
self._sa.apply_driver_hacks(self._app, sa_url, options)
File "/Users/username/venv/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 877, in apply_driver_hacks
if sa_url.drivername.startswith('mysql'):
AttributeError: 'NoneType' object has no attribute 'drivername'
Now what's strange here is that when I print print(app.config) inside the create_app function, the configurations are in place, just like I want them to be. So for example SQLALCHEMY_DATABASE_URI=postgresql://testuser:testpassword#localhost:5432/testdb'. However, when I print the same info inside the app.app_context() loop, SQLALCHEMY_DATABASE_URI=None (as an example, the other key-value pairs are also reset).
What am I missing here?
You are loading your .env file incorrectly:
basedir = os.path.abspath(__file__)
load_dotenv(os.path.join(basedir, '.env'))
basedir is the module file itself, not the directory. So for a file named config.py, basedir is set to /..absolutepath../config.py, not /..absolutepath../config.py. As a result, you are asking dotenv() to load the file /..absolutepath../config.py/.env, which won't exist.
You are missing a os.path.dirname() call:
basedir = os.dirname(os.path.abspath(__file__))
You can avoid this issue altogether by using dotenv.find_dotenv():
load_dotenv(find_dotenv())
which uses the __file__ attribute of the module it is called from.
Other remarks:
You are using Flask-Migrate to manage the schema, so don't call db.create_all(). Instead use the Flask CLI and the Flask Migrate commands (flask db init, flask db migrate, and flask db upgrade to create a migration directory and your initial migration script and then upgrade your connected database to use the latest schema version.
If you are configuring your database from environment variables, then don't use a variable config object. It's easier to work with a Flask app factory if you just import config from the current project for default configuration that applies to all development. You can always load in more configuration via app.config.from_obj() or app.config.from_envvar(), using optional arguments and an environment variable, as needed.
Related
I recently updated Flask-SQLAlchemy, and now db.create_all is raising RuntimeError: working outside of application context. How do I call create_all?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///project.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
db.create_all()
This raises the following error:
Traceback (most recent call last):
File "/home/david/Projects/flask-sqlalchemy/example.py", line 11, in <module>
db.create_all()
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 751, in create_all
self._call_for_binds(bind_key, "create_all")
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 722, in _call_for_binds
engine = self.engines[key]
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 583, in engines
app = current_app._get_current_object() # type: ignore[attr-defined]
File "/home/david/Projects/flask-sqlalchemy/.venv/lib/python3.10/site-packages/werkzeug/local.py", line 513, in _get_current_object
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
As of Flask-SQLAlchemy 3.0, all access to db.engine (and db.session) requires an active Flask application context. db.create_all uses db.engine, so it requires an app context.
with app.app_context():
db.create_all()
When Flask handles requests or runs CLI commands, a context is automatically pushed. You only need to push one manually outside of those situations, such as while setting up the app.
Instead of calling create_all in your code, you can also call it manually in the shell. Use flask shell to start a Python shell that already has an app context and the db object imported.
$ flask shell
>>> db.create_all()
Or push a context manually if using a plain python shell.
$ python
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Here's an example.py that configures a SQLite database, a model, then creates the database. The with app.app_context() line around db.create_all() is what's needed to avoid the context error.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db = SQLAlchemy()
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
with app.app_context():
db.create_all()
Run the development server with this command, and the database will be created if it doesn't exist.
$ flask -A example.py --debug run
If you're using a python shell instead of flask shell, you can push a context manually. flask shell will handle that for you.
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Learn more about the application context in the Flask docs or this video.
I recently updated Flask-SQLAlchemy, and now db.create_all is raising RuntimeError: working outside of application context. How do I call create_all?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///project.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
db.create_all()
This raises the following error:
Traceback (most recent call last):
File "/home/david/Projects/flask-sqlalchemy/example.py", line 11, in <module>
db.create_all()
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 751, in create_all
self._call_for_binds(bind_key, "create_all")
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 722, in _call_for_binds
engine = self.engines[key]
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 583, in engines
app = current_app._get_current_object() # type: ignore[attr-defined]
File "/home/david/Projects/flask-sqlalchemy/.venv/lib/python3.10/site-packages/werkzeug/local.py", line 513, in _get_current_object
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
As of Flask-SQLAlchemy 3.0, all access to db.engine (and db.session) requires an active Flask application context. db.create_all uses db.engine, so it requires an app context.
with app.app_context():
db.create_all()
When Flask handles requests or runs CLI commands, a context is automatically pushed. You only need to push one manually outside of those situations, such as while setting up the app.
Instead of calling create_all in your code, you can also call it manually in the shell. Use flask shell to start a Python shell that already has an app context and the db object imported.
$ flask shell
>>> db.create_all()
Or push a context manually if using a plain python shell.
$ python
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Here's an example.py that configures a SQLite database, a model, then creates the database. The with app.app_context() line around db.create_all() is what's needed to avoid the context error.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db = SQLAlchemy()
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
with app.app_context():
db.create_all()
Run the development server with this command, and the database will be created if it doesn't exist.
$ flask -A example.py --debug run
If you're using a python shell instead of flask shell, you can push a context manually. flask shell will handle that for you.
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Learn more about the application context in the Flask docs or this video.
I recently updated Flask-SQLAlchemy, and now db.create_all is raising RuntimeError: working outside of application context. How do I call create_all?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///project.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
db.create_all()
This raises the following error:
Traceback (most recent call last):
File "/home/david/Projects/flask-sqlalchemy/example.py", line 11, in <module>
db.create_all()
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 751, in create_all
self._call_for_binds(bind_key, "create_all")
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 722, in _call_for_binds
engine = self.engines[key]
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 583, in engines
app = current_app._get_current_object() # type: ignore[attr-defined]
File "/home/david/Projects/flask-sqlalchemy/.venv/lib/python3.10/site-packages/werkzeug/local.py", line 513, in _get_current_object
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
As of Flask-SQLAlchemy 3.0, all access to db.engine (and db.session) requires an active Flask application context. db.create_all uses db.engine, so it requires an app context.
with app.app_context():
db.create_all()
When Flask handles requests or runs CLI commands, a context is automatically pushed. You only need to push one manually outside of those situations, such as while setting up the app.
Instead of calling create_all in your code, you can also call it manually in the shell. Use flask shell to start a Python shell that already has an app context and the db object imported.
$ flask shell
>>> db.create_all()
Or push a context manually if using a plain python shell.
$ python
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Here's an example.py that configures a SQLite database, a model, then creates the database. The with app.app_context() line around db.create_all() is what's needed to avoid the context error.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db = SQLAlchemy()
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
with app.app_context():
db.create_all()
Run the development server with this command, and the database will be created if it doesn't exist.
$ flask -A example.py --debug run
If you're using a python shell instead of flask shell, you can push a context manually. flask shell will handle that for you.
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Learn more about the application context in the Flask docs or this video.
I recently updated Flask-SQLAlchemy, and now db.create_all is raising RuntimeError: working outside of application context. How do I call create_all?
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///project.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
db.create_all()
This raises the following error:
Traceback (most recent call last):
File "/home/david/Projects/flask-sqlalchemy/example.py", line 11, in <module>
db.create_all()
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 751, in create_all
self._call_for_binds(bind_key, "create_all")
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 722, in _call_for_binds
engine = self.engines[key]
File "/home/david/Projects/flask-sqlalchemy/src/flask_sqlalchemy/extension.py", line 583, in engines
app = current_app._get_current_object() # type: ignore[attr-defined]
File "/home/david/Projects/flask-sqlalchemy/.venv/lib/python3.10/site-packages/werkzeug/local.py", line 513, in _get_current_object
raise RuntimeError(unbound_message) from None
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
As of Flask-SQLAlchemy 3.0, all access to db.engine (and db.session) requires an active Flask application context. db.create_all uses db.engine, so it requires an app context.
with app.app_context():
db.create_all()
When Flask handles requests or runs CLI commands, a context is automatically pushed. You only need to push one manually outside of those situations, such as while setting up the app.
Instead of calling create_all in your code, you can also call it manually in the shell. Use flask shell to start a Python shell that already has an app context and the db object imported.
$ flask shell
>>> db.create_all()
Or push a context manually if using a plain python shell.
$ python
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Here's an example.py that configures a SQLite database, a model, then creates the database. The with app.app_context() line around db.create_all() is what's needed to avoid the context error.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db = SQLAlchemy()
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.String)
with app.app_context():
db.create_all()
Run the development server with this command, and the database will be created if it doesn't exist.
$ flask -A example.py --debug run
If you're using a python shell instead of flask shell, you can push a context manually. flask shell will handle that for you.
>>> from project import app, db
>>> app.app_context().push()
>>> db.create_all()
Learn more about the application context in the Flask docs or this video.
I am currently following this pattern to avoid circular imports. This pattern seems to be pretty niche so it's been difficult trying to find a solution googling.
models.py
from sqlalchemy.orm import relationship
db = SQLAlchemy()
class Choice(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(32))
votes = relationship("Vote")
def __init__(self, text):
self.text = text
application.py
app = Flask(__name__)
app.debug = True
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'db.sqlite3')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app) # Reinitialiez db.Model to avoid cyclical imports in models.py
db_create.py
#!flask/bin/python
from application import app, db
from models import Choice
db.init_app(app)
db.create_all()
db.session.add(Choice("Red"))
db.session.add(Choice("Blue"))
db.session.add(Choice("Green"))
db.session.commit()
When I run db_create.py separately, I get:
$ python db_create.py
Traceback (most recent call last):
File "db_create.py", line 6, in <module>
db.create_all()
File "/home/ubuntu/.virtualenvs/paw-py/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 972, in create_all
self._execute_for_all_tables(app, bind, 'create_all')
File "/home/ubuntu/.virtualenvs/paw-py/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 949, in _execute_for_all_tables
app = self.get_app(app)
File "/home/ubuntu/.virtualenvs/paw-py/local/lib/python2.7/site-packages/flask_sqlalchemy/__init__.py", line 922, in get_app
raise RuntimeError('application not registered on db '
RuntimeError: application not registered on db instance and no application bound to current context
What should I do? What is the best pattern to deal with this? I've even tried adding db.create_all() in my application.py after if __name__ == '__main__' but I am still getting the same error
Tell Flask-SQLAlchemy which is "current" app with app_context
with app.app_context():
# your code here
db.create_all()