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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for flask-sqlalchemy-unchained-0.6.2.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 4aafd72eff15a1248599e8cf24a07b135fb6edf58b1249913f19224acb2872ac |
|
MD5 | 6d04f25c1a4dff1aea3b0c42d98c3586 |
|
BLAKE2b-256 | 6ba4dbf22d3836560208b268495bd11e1735ef8821c5ff773c39512bcf498d10 |
Hashes for flask_sqlalchemy_unchained-0.6.2-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 5c244e6ee83b80a1778084561e93235ab5b17d34f4aa7c57b7edba9fdc93ccdd |
|
MD5 | 8fbc27af7d4954813f5bce5e79e0f4c7 |
|
BLAKE2b-256 | 461c3de8329ffc6459df155044b938af62d88d7ccb706d49756e56b10e2bf1c6 |