def test_tablename(self, db): class NotLazy(db.Model): class Meta: abstract = True lazy_mapped = False class Auto(NotLazy): pass assert Auto.Meta.table is None assert '__tablename__' not in Auto.Meta._mcs_args.clsdict assert Auto.__tablename__ == 'auto' class DeclaredAttr(NotLazy): @db.declared_attr def __tablename__(cls): return cls.__name__.lower() assert DeclaredAttr.Meta.table is None assert DeclaredAttr.__tablename__ == 'declaredattr' class Manual(NotLazy): __tablename__ = 'manuals' assert Manual.Meta.table == 'manuals' assert Manual.Meta._mcs_args.clsdict['__tablename__'] == 'manuals' assert Manual.__tablename__ == 'manuals' class AutoMV(db.MaterializedView): @classmethod def selectable(cls): return db.select([Auto.id]) UnchainedModelRegistry().finalize_mappings() assert AutoMV.Meta.table == 'auto_mv' assert AutoMV.__table__.fullname == 'auto_mv' assert AutoMV.Meta._mcs_args.clsdict['__tablename__'] == 'auto_mv' assert AutoMV.__tablename__ == 'auto_mv' class ManualMV(db.MaterializedView): class Meta: table = 'manual_materialized_view' @classmethod def selectable(cls): return db.select([Manual.id]) UnchainedModelRegistry().finalize_mappings() assert ManualMV.Meta.table == 'manual_materialized_view' assert ManualMV.__table__.fullname == 'manual_materialized_view' assert ManualMV.Meta._mcs_args.clsdict['__tablename__'] == \ 'manual_materialized_view' assert ManualMV.__tablename__ == 'manual_materialized_view'
def setup(db): class Node(db.Model): class Meta: repr = ('id', 'name', 'path') slug = db.Column(db.String, index=True, unique=True) parent_id = db.foreign_key('Node', nullable=True) parent = db.relationship('Node', back_populates='children', remote_side='Node.id') children = db.relationship('Node', back_populates='parent') mv = db.relationship('NodeMV', uselist=False, foreign_keys='NodeMV.id', primaryjoin='Node.id == NodeMV.id', viewonly=True) depth = db.association_proxy('mv', 'depth') path = db.association_proxy('mv', 'path') class NodeMV(db.MaterializedView): class Meta: mv_for = 'Node' @classmethod def selectable(cls): _cte = (db.select([ Node.id.label('id'), literal(0).label('depth'), literal('/').label('path') ]).where(Node.parent_id == None).cte(name='nodes_cte', recursive=True)) _union = _cte.union_all( db.select([ Node.id.label('id'), label('depth', _cte.c.depth + 1), label( 'path', case( [ # tuple(if condition, then value), (_cte.c.depth == 0, _cte.c.path + Node.slug), ], else_=_cte.c.path + '/' + Node.slug)), ]).select_from(db.join(_cte, Node, _cte.c.id == Node.parent_id))) return db.select([_union]) unchained.sqlalchemy_bundle.models = UnchainedModelRegistry( ).finalize_mappings() db.create_all() class NodeManager(ModelManager): class Meta: model = Node return Node, NodeMV, NodeManager()
def db_ext(bundles): os.environ['FLASK_ENV'] = TEST os.environ['SQLA_TESTING'] = 'True' sqla_bundle = 'flask_unchained.bundles.sqlalchemy' db_bundles = [ sqla_bundle, 'tests.bundles.sqlalchemy._bundles.custom_extension' ] try: bundle_under_test = [m for m in db_bundles if m in bundles][0] except (IndexError, TypeError): bundle_under_test = sqla_bundle UnchainedModelRegistry()._reset() clear_mappers() unchained._reset() # NOTE: this logic is only correct for one level deep of bundle extension # (the proper behavior from unchained hooks is to import the full # inheritance hierarchy, and that is especially essential for all of the # metaclass magic in this bundle to work correctly) modules_to_import = ([bundle_under_test] if bundle_under_test == sqla_bundle else [sqla_bundle, bundle_under_test]) for module_name in modules_to_import: if module_name in sys.modules: del sys.modules[module_name] db_module = importlib.import_module(module_name) ext_module_name = f'{module_name}.extensions' if ext_module_name in sys.modules: del sys.modules[ext_module_name] db_extensions_module = importlib.import_module(ext_module_name) kwargs = getattr( db_extensions_module, 'kwargs', dict(metadata=MetaData( naming_convention={ 'ix': 'ix_%(column_0_label)s', 'uq': 'uq_%(table_name)s_%(column_0_name)s', 'ck': 'ck_%(table_name)s_%(constraint_name)s', 'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s', 'pk': 'pk_%(table_name)s', }), )) db = db_extensions_module.SQLAlchemy(**kwargs) unchained.extensions.db = db for module in [db_module, db_extensions_module]: setattr(module, 'db', db) EXTENSIONS = getattr(db_extensions_module, 'EXTENSIONS') EXTENSIONS['db'] = db setattr(db_extensions_module, 'EXTENSIONS', EXTENSIONS) yield db
def setup(db: SQLAlchemy): class Foo(db.Model): name = db.Column(db.String) # simulate the register models hook unchained.sqlalchemy_bundle.models['Foo'] = Foo class FooManager(ModelManager): class Meta: model = Foo UnchainedModelRegistry().finalize_mappings() db.create_all() return Foo, FooManager()