Using SQLAlchemy with MySQL
SQLAlchemy is a really useful ORM/connection manager that makes it much easier to connect to databases from your code, especially if you're writing a website using a framework with no built-in database management tools -- for example, if you're using Flask.
Connecting to MySQL using SQLAlchemy¶
SQLAlchemy expects a URI to tell it how to connect to the MySQL database. This is of the format
"mysql+mysqldb://{username}:{password}@{hostname}/{databasename}"
...where the {username}
is the one shown on the "MySQL" tab of the databases
page inside PythonAnywhere, the {password}
is the one you specified on that
tab, the {hostname}
is also the one from that tab, and the {databasename}
is
the name of one of your databases -- don't forget that the database name starts
with your username, then a dollar sign, and then the part of the name that you
specified when you created it.
Configuring SQLAlchemy¶
One particular setting that you need to get right if you're using it is the
pool_recycle
. This tells SQLAlchemy how long a database connection can be
left unused before it should be discarded; it's important because connections
get closed on the server side if they're inactive for more than a specific amount
of time -- 300 seconds on PythonAnywhere. If you try to use a connection that
has been closed that way, you'll get an error like this:
2013, 'Lost connection to MySQL server during query'
How to configure Flask-SQLAlchemy¶
If you're using Flask-SQLAlchemy, you need to specify the pool_recycle
setting
when you initialise the SQLAlchemy
object. For example:
db = SQLAlchemy() app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {'pool_recycle' : 280} db.init_app(app)
Or
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {'pool_recycle' : 280} db = SQLAlchemy(app)
For older versions (before version 2.4.0), you use slightly different configuration:
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI app.config["SQLALCHEMY_POOL_RECYCLE"] = 280 db.init_app(app)
Using SQLAlchemy directly¶
If you're using SQLAlchemy directly, you configure it like this:
engine = create_engine('mysql+mysqldb://...', pool_recycle=280)
Using Flask-SQLAlchemy outside view functions in websites¶
Sometimes you will continue to get connection errors when using Flask-SQLAlchemy in a
website even when you've set pool_recycle
correctly. They can look like the
one above, or they can look like this:
sqlalchemy.exc.OperationalError: (mysql.connector.errors.OperationalError) MySQL Connection not available.
The most common cause of this is if you are accessing the database from outside a view function.
The problem is caused by the way websites are loaded up on PythonAnywhere. When a website’s code is started, we spin up one process for it, which loads up all of your code, doing all of the imports and so on. As as side-effect, this will run all code that is outside view functions.
Once that’s done, and the code is all loaded, we fork off the multiple worker processes that handle incoming requests to your site.
What that means is that if you do some DB access through SQLAlchemy outside your views, a connection to the database will be created before the fork, and then each forked process will have a copy of the same connection. Then, if multiple processes try to use that connection, they’ll interfere with each other and you’ll get a connection error.
The best solution to this is simply to not access the database from code outside view functions.
However, a hack that should work if you really need to access the DB while your code is starting up is to do this afterwards:
db.session.close() db.get_engine(app).dispose()