Source code for coaster.db

# -*- coding: utf-8 -*-

from __future__ import absolute_import
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import NoResultFound
from flask.ext.sqlalchemy import SQLAlchemy as SQLAlchemyBase, SignallingSession

__all__ = ['SQLAlchemy', 'db']


[docs]class CoasterSession(SignallingSession): """ Custom session that provides additional helper methods. """
[docs] def add_and_commit(self, _instance, **filters): """ Add and commit a new instance, gracefully handling failure in case a conflicting entry is already in the database (which may occur due to parallel requests causing race conditions in a production environment with multiple workers). Returns the instance saved to database if no error occured, or loaded from database using the provided filters if an error occured. If the filters fail to load from the database, the original IntegrityError is re-raised, as it is assumed to imply that the commit failed because of missing or invalid data, not because of a duplicate entry. Usage: ``db.session().add_and_commit(instance, **filters)`` where filters are the parameters passed to ``Model.query.filter_by(**filters).one()`` to load the instance. :param _instance: Instance to commit :param filters: Filters required to load existing instance from the database in case the commit fails (required) :return: Instance that is in the database """ self.begin_nested() try: self.add(_instance) self.commit() return _instance except IntegrityError as e: self.rollback() try: return self.query(_instance.__class__).filter_by(**filters).one() except NoResultFound: # Do not trap the other exception, MultipleResultsFound raise e # Provide a Flask-SQLAlchemy alternative that uses our custom session
[docs]class SQLAlchemy(SQLAlchemyBase): """ Subclass of :class:`flask.ext.sqlalchemy.SQLAlchemy` that uses :class:`CoasterSession`, providing additional methods in the database session. """
[docs] def create_session(self, options): """ Creates the session using :class:`CoasterSession`. """ return CoasterSession(self, **options)
db = SQLAlchemy()