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.2.tar.gz (6.3 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.6.2-py3-none-any.whl (4.5 kB view details)

Uploaded Python 3

File details

Details for the file flask-sqlalchemy-unchained-0.6.2.tar.gz.

File metadata

  • Download URL: flask-sqlalchemy-unchained-0.6.2.tar.gz
  • Upload date:
  • Size: 6.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.25.0 CPython/3.7.0

File hashes

Hashes for flask-sqlalchemy-unchained-0.6.2.tar.gz
Algorithm Hash digest
SHA256 4aafd72eff15a1248599e8cf24a07b135fb6edf58b1249913f19224acb2872ac
MD5 6d04f25c1a4dff1aea3b0c42d98c3586
BLAKE2b-256 6ba4dbf22d3836560208b268495bd11e1735ef8821c5ff773c39512bcf498d10

See more details on using hashes here.

File details

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

File metadata

  • Download URL: flask_sqlalchemy_unchained-0.6.2-py3-none-any.whl
  • Upload date:
  • Size: 4.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.25.0 CPython/3.7.0

File hashes

Hashes for flask_sqlalchemy_unchained-0.6.2-py3-none-any.whl
Algorithm Hash digest
SHA256 5c244e6ee83b80a1778084561e93235ab5b17d34f4aa7c57b7edba9fdc93ccdd
MD5 8fbc27af7d4954813f5bce5e79e0f4c7
BLAKE2b-256 461c3de8329ffc6459df155044b938af62d88d7ccb706d49756e56b10e2bf1c6

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