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:
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.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.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With