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.get(self.model, 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.8.0.tar.gz (4.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flask_sqlalchemy_unchained-0.8.0-py3-none-any.whl (5.2 kB view details)

Uploaded Python 3

File details

Details for the file flask_sqlalchemy_unchained-0.8.0.tar.gz.

File metadata

  • Download URL: flask_sqlalchemy_unchained-0.8.0.tar.gz
  • Upload date:
  • Size: 4.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.11.6 Linux/6.6.6-arch1-1

File hashes

Hashes for flask_sqlalchemy_unchained-0.8.0.tar.gz
Algorithm Hash digest
SHA256 f47601f8b845ce4ae5e195ff816ab778ece4dd8b0dcfeaf5c87c4efbba091548
MD5 b3e062451a1d89b34d66396b2a860fda
BLAKE2b-256 e0587ff746e3a1b5576bfd85a0eefa7e9b567d801f96bff984c92920d591c154

See more details on using hashes here.

File details

Details for the file flask_sqlalchemy_unchained-0.8.0-py3-none-any.whl.

File metadata

File hashes

Hashes for flask_sqlalchemy_unchained-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e877543d60b6cfbbe321507df67be3b50e8754c5bfec6a1087ee17eb0dfbb555
MD5 1f2cb521891f75ecc081855fa0f679fb
BLAKE2b-256 1c56e013bde58b98774d98f71b6a9727b4e7f57a440ddc4a0911c1b8f646d28d

See more details on using hashes here.

Supported by

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