Use Flask and SQLalchemy, not Flask-SQLAlchemy! | by Edward Krueger | Towards Data Science

By: Edward Krueger Data Scientist and Instructor and Douglas Franklin Teaching Assistant and Technical Writer.
In this article, we will cover using Flask with SQLAlchemy and some reasons to avoid Flask-SQLAlchemy. Additionally, we’ll demonstrate the benefits of having SQLAlchemy models and database connections that exist independently of an app.
SQLAlchemy is a Python SQL toolkit and Object Relational Mapper (ORM) that allows app developers to use SQL for smooth and fault-tolerant transactional database operations. The ORM translates Python classes to tables for relational databases and automatically converts Pythonic SQLAlchemy Expression Language to SQL statements. This conversion allows developers to write SQL queries with Python syntax. SQLAlchemy also abstracts database connections and provides connection maintenance automatically. Together these features make SQLAlchemy a fantastic package for loading and querying databases.
Flask is a microframework that allows you to build web apps in Python. Flask is easy to get started with as a beginner because there is little boilerplate code for getting a simple app up and running.
For example, here is a valid “Hello, world!” Flask web app:
from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world():    return "Hello, World!"if __name__ == '__main__':    app.run()
Flask-SQLAlchemy is an extension for Flask that aims to simplify using SQLAlchemy with Flask by providing defaults and helpers to accomplish common tasks. One of the most sought after helpers being the handling of a database connection across the app. However, ensuring your database connection session is available throughout your app can be accomplished with base SQLAlchemy and does not require Flask-SQLAlchemy.
Flask-SQLAlchemy’s purpose is to handle the return of connections to prevent issues with worker threading. These issues arise when an app user switches from one route to another. Below is a common error that occurs when a threading problem is present in a Flask app.
sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 12345 and this is thread id 54321.
This is often caused by a session or database connection being unavailable to part of your app. This error effectively breaks your app and must be resolved to continue development.
We opt to avoid the Flask-SQLALchemy default behaviors that prevent this error and instead use SQLAlchemy features to handle these issues. This is because Flask-SQLALchemy has disadvantages when compared with SQLAlchemy.
One of which is that Flask-SQLAlchemy has its own API. This adds complexity by having its different methods for ORM queries and models separate from the SQLAlchemy API.
Another disadvantage is that Flask-SQLAlchemy makes using the database outside of a Flask context difficult. This is because, with Flask-SQLAlchemy, the database connection, models, and app are all located within the app.py file. Having models within the app file, we have limited ability to interact with the database outside of the app. This makes loading data outside of your app difficult. Additionally, this makes it hard to retrieve data outside of the Flask context.
Flask and SQLAlchemy work well together if used correctly. Therefore, you don’t have to hybridize Flask and SQLAlchemy into Flask-SQLalchemy!
Ideally, you should only have to define your database models once! With a separate database.py and models.py file, we can establish our database connection and classes for its tables a single time, then call them later as needed. This separation of components is much more difficult with Flask-SQLAlchemy as you would have to create the database with the app itself.
Here is a file that defines our database connection using SQLAlchemy.
Notice that we import the ‘Base’ class in the database.py file above into the models.py file below to use declarative_base().
This file creates the model or schema for the table ‘Records’ in our database.
Using SQLAlcehmy’s declarative_base() allows you to write just one model per table that app uses. That model is then used in Python outside of the app and in the database.
Having these separate Python files is good because you can use the same model to query or load data outside of an app. Additionally, you’ll have one version of each model and database connection, which simplifies development.
These models and database connections can be used to reference the same models or databases in data pipelines, report generation, or anywhere else they are needed.
The load script alone is a great reason to use SQLAlchemy. Instead of using the app to load data, this functionality allows the loading of a database with a separate Python file.
Here is an example Python file that reads data from a CSV and inserts that data into a database.
Notice that we import the models, our custom session SessionLocal, and our engine that we’ve defined in other Python files.
This separate load.py file allows us to insert data into the database without ever running the app.
The declarative_base() base class contains a MetaData object where newly defined Table objects are collected. This MetaData object is accessed when we call the line models.Base.metadata.create_all()to create all of our tables.
SQLAlchemy includes a helper object that helps with the establishment of user-defined Session scopes. With the scoped_session function, SQLAlchemy can handle worker threading issues.
The sessionmaker is a factory for initializing new Session objects by requesting a connection from the engine’s connection pool and attaching a connection to the new Session object.
Initializing a new session object is also referred to as “checking out” a connection. The database stores a list of these connections/processes. So when you begin a new session, you are starting a new process within the database too.
A scoped_session is the registry of all these created session objects where the key/identity of the registry is some form of a thread-safe id.
We define SessionLocal in the database.py file above by calling the session factory, sessionmaker, and passing it some parameters.
The`scopefunc’ is an optional argument passed to scoped_session that serves as an identity getter function that returns a key to lookup or to register a new session. Adding `_app_ctx_stack.__ident_func__` is one of two functions:
If greenlet is installed, it uses the getcurrent (from greenlet import getcurrent)Otherwise, it uses get_ident (from threading import get_ident), which returns the thread id.
If greenlet is installed, it uses the getcurrent (from greenlet import getcurrent)
Otherwise, it uses get_ident (from threading import get_ident), which returns the thread id.
By default scopefunc is get_ident . So for simple applications, you can just do:
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
This new SessionLocal allows different sections of the application to call upon a global scoped_session, so that all app routes can share the same session without passing the session to the route explicitly. The SessionLocal we've established in our registry will remain, until we explicitly tell our registry to dispose of it, by calling scoped_session.remove():
Here we can see the call to scoped_session with our custom session, SessionLocal, passed as an argument.
Additionally, scoped sessions give us access to a query_property. So if you style used by flask_sqlalchemy you can use this with SQLAlchemy:
Base = declarative_base()Base.query = db_session.query_property()
The method db_session.close() only ends the transaction for the local session object, but does not end the connection with the database and does not automatically return the connection to the pool.
By adding a db_session.remove() we ensure our connection is closed properly.
The db_session.remove() method first runs app.db_session.close() and afterward returns the connection to the connection pool. Therefore we have terminated that process locally and at the remote database. If the database doesn't have these connections closed, there is a maximum number of connections that can be reached. The database will eventually kill idle processes like stale connections; however, it can take hours before that happens. SQLAlchemy has some pool options to prevent this, but removing the connections when they are no longer needed is best!
As we have seen, SQLAlchmy has the tools to handle errors that developers turn to Flask-SQLAlchemy to avoid. With the proper implementation of sessionmaker and scoped_session, your Flask app should not have any threading issues that arise when connecting to a database across your routes.
So when struggling with threading and database sessions, use Flask and SQLAlchemy, not Flask-SQLAlchemy!
Special thanks to Seth Kaufman for help writing our Flask app, be sure to check out the repository on GitHub.