Share sqlalchemy models between flask and other apps

The Flask-SQLAlchemy extension, like most Flask extensions, should be created outside the factory, then initialized in the factory using init_app. This is so that you can use the db object before an app is created.

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    db.init_app(app)
    return app

Your Flask app, like any properly designed Python project, should be an installable package. This is simple to do: make sure your project layout makes sense, then add a basic setup.py file.

project/
    my_flask_package/
        __init__.py  # at the most basic, this contains create_app and db
    setup.py
from setuptools import setup, find_packages

setup(
    name='my_flask_package',
    version='1.0',
    packages=find_packages(),
    install_requires=['flask', 'flask-sqlalchemy'],
)
$ python setup.py sdist

Now you can install your Flask app, along with it's database, for use in other projects. Install and import it in your second project's virtualenv, then create and push an app to initialize it.

$ pip install my_flask_package-1.0.tar.gz
from my_flask_package import db, create_app
create_app().app_context().push()
db.session.query(...)

If you're concerned about overhead involved in creating your application, you could add arguments to the create_app function to control what gets initialized. For most cases this shouldn't be an issue though.


I ran into the same problem.

If you turn on "SQLALCHEMY_ECHO" you'll likely see that a new transaction is started but the corresponding COMMIT/ ROLLBACK is missing.

For what i found out, it has something to do with two SQLAlchemy instances which you also create, once in your model file and once in your web.py. Most likely it's because you interact with your web.py's session and if you query your models there is some context switched which will receive the COMMIT.

I fixed the issue by importing "db" from models and then init it by calling db.init_app(app). According to the logs, committing now works fine.

The @app.teardown_appcontext shouldn't be necessary as it is set up in Flask-SQLAlchemy's SQLAlchemy class (https://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy/init.py)


You can easily share. I will show how. Considering this Flask app:

.
├── config.py
├── db
│   └── test.db
├── do_somenthing2.py ============> Here is run some script 2
├── do_something.py   ============> Here is run some script
├── machinelearning
│   ├── models
│   │   ├── restore.py
│   │   ├── train.py
│   │   └── utils.py
│   └── save
│       └── test.ckpt
├── runserver.py ============> Here is run your app
├── test.py
└── web
    ├── __init__.py
    ├── api
    │   ├── __init__.py
    │   ├── app.py  ============> Here is app = Flask(__name__)
    │   ├── client.py
    │   ├── models.py ==========> Here is db = SQLAlchemy(app)
    │   ├── sample.json
    │   └── utils.py
    └── frontend
        ├── __init__.py
        └── routes.py

runserver.py

import os

from config import DEBUG

from web.api.app import app
from web.api.client import *

if __name__ == "__main__":
    app.run(debug=DEBUG)

OK. Now you want do use the same models to do another thing. For example: train a machine, serve and save in database (ORM) using the same models.

You can import the app and use the app.test_request_context(). Like that:

do_something.py

from web.api.app import app from web.api.models import db, user

def do_something():
    q = db.session.query(User)\
        .filter(User.Name.ilike('Andre'))
    for i in q.all():
        print (i.Name)    

with app.test_request_context():
    do_something()

do_something2.py (real example)

from web.api.app import app
from web.api.models import *

def save(df):

    passengers = []

    records = df.to_dict('records')

    for row in records:
        p = Passenger(row)
        passengers.append(p)

    for p in passengers:
        db.session.add(p)

    db.session.commit()

from ml.models import train, restore

with app.test_request_context():
    print ('Trainning model. It will take a while... (~ 5 minutos)')
    train.run()
    print ('Saving model...')
    save(restore.run())
    print ('Saved!')

Many answers recommend use (Importing files from different folder):

import sys
sys.path.append('../')

But I disagree when you have a Flask app and other scripts, because you will get crazy solving the relative references.

The approach to install your Flask app, along with it's database, for use in other projects, is another option.

Here you can find a documentation about the packages and modules.

Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name A.B designates a submodule named B in a package named A. Just like the use of modules saves the authors of different modules from having to worry about each other’s global variable names, the use of dotted module names saves the authors of multi-module packages like NumPy or Pillow from having to worry about each other’s module names.