flask.py - Provide extensions for Flask-SQLAlchemy

See test_flask.py - Unit tests for ../pythonic_sqlalchemy_query/flask.py for usage and examples.

Imports

These are listed in the order prescribed by PEP 8.

Standard library

None.

 

Third-party imports

from sqlalchemy.ext.declarative import declarative_base
from flask import _app_ctx_stack
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import Model, DefaultMeta
 

Local imports

from . import QueryMakerScopedSession, QueryMaker
 
 

Flask-SQLAlchemy customization

Create a SQLAlchemy class that includes the pythonic_sqlalchemy_query extension. In particular, it supports the following improvements over Flask-SQLAlchemy’s queries:

  • User['peter'].q.first() as a shortcut for User.query.filter_by(username='peter').first().
  • db.session(User)['peter'].q.first() as a shortcut to db.session.query(User).filter_by(username='peter').first().

To use: db = SQLAlchemyPythonicQuery(app) instead of db = SQLAlchemy(app), as shown in test_flask.py - Unit tests for ../pythonic_sqlalchemy_query/flask.py.

Enable syntax such as Model[id] for queries.

class QueryMakerFlaskDeclarativeMeta(DefaultMeta):
    def __getitem__(cls, key):

Extract the session from the Flask-SQLAlchemy query attribute.

        session = cls.query.session

Build a new query from here, since cls.query has already invoked add_entity on cls.

        return QueryMaker(cls, session.query())[key]
 
 

Then, use this in the Flask-SQLAlchemy session.

class SQLAlchemyPythonicQuery(SQLAlchemy):

Unlike the standard SQLAlchemy parameters, this does not allow the model_class keyword argument.

    def __init__(self, *args, **kwargs):

Provide a declarative_base model for Flask-SQLAlchemy. This is almost identical to code in flask_sqlalchemy.SQLAlchemy.make_declarartive_base, but using our custom metaclass.

        assert 'model_class' not in kwargs
        kwargs['model_class'] = declarative_base(
            cls=Model,
            name='Model',
            metadata=kwargs.get('metadata', None),

Use our custom metaclass.

            metaclass=QueryMakerFlaskDeclarativeMeta
        )

        super(SQLAlchemyPythonicQuery, self).__init__(*args, **kwargs)
 

Enable syntax such as db.session(User)['peter']. The only change from the Flask-SQLAlchemy v2.3.2 source: QueryMakerScopedSession instead of orm.scoped_session.

    def create_scoped_session(self, options=None):
        if options is None:
            options = {}

        scopefunc = options.pop('scopefunc', _app_ctx_stack.__ident_func__)
        options.setdefault('query_cls', self.Query)
        return QueryMakerScopedSession(
            self.create_session(options), scopefunc=scopefunc
        )