Skip to content

Instantly share code, notes, and snippets.

@aklos
Created January 5, 2016 15:49
Show Gist options
  • Select an option

  • Save aklos/6c4afd656d1c8fdf1dad to your computer and use it in GitHub Desktop.

Select an option

Save aklos/6c4afd656d1c8fdf1dad to your computer and use it in GitHub Desktop.
Flask SQLAlchemy default model filters
# Example model
class User(Base):
__tablename__ = 'user'
@classmethod
def _base_filters(self, obj):
# Add this method to your model if you want base filtering, otherwise leave it out
# import and_ from sqlalchemy package
# this is a base filter for ALL queries
return and_(
obj.country == 'puerto rico',
obj.email != '[email protected]'
)
# The custom Query
class BaseFilterQuery(Query):
def get(self, ident):
# Override get() so that the flag is always checked in the
# DB as opposed to pulling from the identity map. - this is optional.
return Query.get(self.populate_existing(), ident)
def __iter__(self):
return Query.__iter__(self.private())
def from_self(self, *ent):
# Override from_self() to automatically apply
# the criterion to. this works with count() and
# others.
return Query.from_self(self.private(), *ent)
def private(self):
# Fetch the model name and column list and apply model-specific base filters
mzero = self._mapper_zero()
if mzero:
# Sometimes a plain model class will be fetched instead of mzero
try:
model = mzero.class_
obj = mzero.class_
except Exception as e:
model = mzero.__class__
obj = mzero
if hasattr(model, '_base_filters'):
return self.enable_assertions(False).filter(model._base_filters(obj))
return self
# Apply to flask session
db = SQLAlchemy(app, session_options={'query_cls': BaseFilterQuery})
# Otherwise..
# sessionmaker(bind=engine, query_cls=BaseFilterQuery)
@sponde-squirrel
Copy link

sponde-squirrel commented Apr 3, 2021

Hello,
I have overridden query_cls using session_maker.
But in my case, _base_filters is not called.

Models:

from typing import Any
from sqlalchemy.ext.declarative import as_declarative, declared_attr

@as_declarative()
class Base:
    id: Any
    __name__: str
    # Generate __tablename__ automatically
    @declared_attr
    def __tablename__(cls) -> str:
        return cls.__name__.lower()


class Organization(Base):
    __tablename__ = 'organization'
    id = Column(Integer, primary_key=True, index=True)
    organization_name = Column(VARCHAR(50), index=True, nullable=False)

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, index=True)
    organization_id = Column(Integer, ForeignKey('organization.id'))

    @classmethod
    def _base_filters(cls, obj):
        logging.info('base filter')
        # Add this method to your model if you want base filtering, otherwise leave it out
        # import and_ from sqlalchemy package
        # this is a base filter for ALL queries
        return obj.organization_id == 1

BaseFilterQuery is same as yours

Session:

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine, query_cls=BaseFilterQuery)

What is wrong?

@aklos
Copy link
Author

aklos commented Apr 23, 2021

Heya, this snippet was written 5 years ago, so SQLAlchemy has likely gone through quite a few changes since then. I recommend searching for query_cls and seeing what the code currently looks like. You might just need to make a few changes to BaseFilterQuery to get it working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment