Skip to main content

Integrates SQLAlchemy Unchained with Flask

Project description

Flask SQLAlchemy Unchained

Integrates SQLAlchemy Unchained with Flask. This package is a very thin wrapper around Flask-SQLAlchemy, and in terms of registering the extension with Flask, everything is the same.

Basic Usage

# your_app.py

from flask import Flask
from flask_sqlalchemy_unchained import SQLAlchemyUnchained


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
db = SQLAlchemyUnchained(app)


class User(db.Model):
    class Meta:
        repr = ('id', 'username', 'email')

    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

Now let's create the table and add a row:

export FLASK_APP='./your_app.py'
export FLASK_DEBUG='true'
flask shell
>>> from your_app import db, User
>>> db.create_all()
>>> user = User(username='fooar', email='foo@bar.com')
>>> db.session.add(user)
>>> db.session.commit()
>>> assert User.query.all() == [user]

Real-World Usage

Now let's take a look at using the application factory pattern. Our app's directory structure will look like this:

./your-project
├── app
│   ├── models
│   │   ├── __init__.py
│   │   └── user.py
│   ├── services
│   │   ├── __init__.py
│   │   ├── model_manager.py
│   │   └── user_manager.py
│   ├── __init__.py
│   ├── config.py
│   ├── extensions.py
│   └── factory.py
├── db
│   └── dev.sqlite
├── tests
│   ├── __init__.py
│   └── test_user.py
├── autoapp.py
└── setup.py

The entry point of our app will be autoapp.py, so let's take a look at that first:

# app/autoapp.py

import os

from app.factory import create_app


app = create_app(os.getenv('FLASK_ENV', 'development'))

And now the app factory:

# app/factory.py
from flask import Flask

from .config import DevConfig, ProdConfig, TestConfig
from .extensions import db


CONFIGS = {
    'development': DevConfig,
    'production': ProdConfig,
    'test': TestConfig,
}


def create_app(env):
    config = CONFIGS[env]

    app = Flask(__name__,
                template_folder=config.TEMPLATE_FOLDER,
                static_folder=config.STATIC_FOLDER,
                static_url_path=config.STATIC_URL_PATH)
    app.config.from_object(config)

    db.init_app(app)

    return app

Which leads us to the config and extensions modules:

# app/config.py

import os


class BaseConfig:
    DEBUG = os.getenv('FLASK_DEBUG', False)

    APP_ROOT = os.path.abspath(os.path.dirname(__file__))
    PROJECT_ROOT = os.path.abspath(os.path.join(APP_ROOT, os.pardir))

    SQLALCHEMY_TRACK_MODIFICATIONS = False


class DevConfig(BaseConfig):
    DEBUG = os.getenv('FLASK_DEBUG', True)

    db_path = os.path.join(BaseConfig.PROJECT_ROOT, 'db', 'dev.sqlite')
    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + db_path


class ProdConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = \
        '{engine}://{user}:{password}@{host}:{port}/{db_name}'.format(
            engine='postgresql+psycopg2',
            user=os.getenv('FLASK_DATABASE_USER', 'sqlalchemy_demo'),
            password=os.getenv('FLASK_DATABASE_PASSWORD', 'sqlalchemy_demo'),
            host=os.getenv('FLASK_DATABASE_HOST', '127.0.0.1'),
            port=os.getenv('FLASK_DATABASE_PORT', 5432),
            db_name=os.getenv('FLASK_DATABASE_NAME', 'sqlalchemy_demo'))


class TestConfig(BaseConfig):
    TESTING = True
    DEBUG = True

    SQLALCHEMY_DATABASE_URI = 'sqlite://'  # :memory:
# app/extensions.py

from flask_sqlalchemy_unchained import SQLAlchemyUnchained


db = SQLAlchemyUnchained()

The User model is the same as before:

# app/models/user.py

from app.extensions import db


class User(db.Model):
    class Meta:
        repr = ('id', 'username', 'email')

    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

Because SQLAlchemy uses the data-mapper pattern, it's best practice to use managers/services for dealing with interactions with the database. A good base to start from might look like this:

# app/services/model_manager.py

from typing import *

from app.extensions import db


class ModelManager:
    model: Type[db.Model]

    def create(self, commit: bool = False, **kwargs) -> db.Model:
        instance = self.model(**kwargs)
        self.save(instance, commit)
        return instance

    def update(self, instance: db.Model, commit: bool = False,
               **kwargs) -> db.Model:
        for attr, value in kwargs.items():
            setattr(instance, attr, value)
        self.save(instance, commit)
        return instance

    def delete(self, instance: db.Model, commit: bool = False) -> None:
        db.session.delete(instance)
        if commit:
            self.commit()

    def save(self, instance: db.Model, commit: bool = True):
        db.session.add(instance)
        if commit:
            self.commit()

    def commit(self) -> None:
        db.session.commit()

    def rollback(self) -> None:
        db.session.rollback()

    def get(self, id) -> db.Model:
        return db.session.query(self.model).get(int(id))

    def get_by(self, **kwargs) -> db.Model:
        return db.session.query(self.model).filter_by(**kwargs).first()

    def find_all(self) -> List[db.Model]:
        return db.session.query(self.model).all()

    def find_by(self, **kwargs) -> List[db.Model]:
        return db.session.query(self.model).filter_by(**kwargs).all()

And then the UserManager class would look like this:

# app/services/user_manager.py

from ..models import User

from .model_manager import ModelManager


class UserManager(ModelManager):
    model = User

    def create(self, username, email, **kwargs) -> User:
        return super().create(username=username, email=email, **kwargs)


user_manager = UserManager()

The full source code for this example app, including integrations with Flask-Migrate and Py-YAML-Fixtures, can be found on GitHub.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flask-sqlalchemy-unchained-0.6.9.tar.gz (6.7 kB view hashes)

Uploaded Source

Built Distribution

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page