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?