def _import_model_classes_from_file(filepath): """ Import the SQLAlchemy models from the Python module at `filepath` """ imported_model_classes = [] mod = import_module_from_file(filepath) # NOTE - We cannot use # pfb_exporter.utils.import_subclass_from_module here because # we are unable to use issubclass to test if the SQLAlchemy model # class is a subclass of its parent # (sqlalchemy.ext.declarative.api.Base) # The best we can do is make sure the class is a SQLAlchemy object # and check that the object is a DeclarativeMeta type for cls_name, cls_path in inspect.getmembers(mod, inspect.isclass): cls = getattr(mod, cls_name) try: sqla_inspect(cls) except NoInspectionAvailable: # Not a SQLAlchemy object pass else: if type(cls) == DeclarativeMeta: imported_model_classes.append(cls) return imported_model_classes
def _create_pfb_schema(self): """ Transform SQLAlchemy models into PFB schema """ self.logger.info('Creating PFB schema from SQLAlchemy models ...') relational_model = {} for model_name, model_cls in self.model_dict.items(): self.logger.info(f'Building schema for {model_name} ...') # Inspect model columns and types for p in sqla_inspect(model_cls).iterate_properties: model_schema = defaultdict(list) if not isinstance(p, ColumnProperty): continue if not hasattr(p, 'columns'): continue column_obj = p.columns[0] # Check if foreign key if column_obj.foreign_keys: fkname = column_obj.foreign_keys.pop().target_fullname model_schema['foreign_keys'].append({ 'table': fkname.split('.')[0], 'name': p.key }) # Convert SQLAlchemy column type to avro type stype = type(column_obj.type).__name__ # Get avro primitive type ptype = SQLA_AVRO_TYPE_MAP['primitive'].get(stype) if not ptype: self.logger.warn(f'⚠️ Could not find avro type for {p}, ' f'SQLAlchemy type: {stype}') attr_dict = {'name': p.key, 'type': ptype} # Get avro logical type if applicable ltype = SQLA_AVRO_TYPE_MAP['logical'].get(stype) if ltype: attr_dict.update({'logicalType': ltype}) # Get default value for attr # if column_obj.default: # attr_dict.update({'default': column_obj.default}) # if column_obj.nullable: # attr_dict.update({'nullable': column_obj.nullable}) model_schema['attributes'].append(attr_dict) relational_model[model_cls.__tablename__] = model_schema return relational_model
def test_init(self): fn = inspect.stack()[0][3] from sqlalchemy.inspection import inspect as sqla_inspect class SomeClass1(TableModel): __tablename__ = "%s_%d" % (fn, 1) i = Integer32(pk=True) e = Unicode(32) from spyne.util.dictdoc import get_dict_as_object inst = get_dict_as_object(dict(i=4), SomeClass1) assert not sqla_inspect(inst).attrs.e.history.has_changes()
def scan_all_relations(klass): relations = [] for rel in sqla_inspect(klass).relationships: relations.append((rel.key, rel.mapper.class_.__name__)) return relations
def scan_relations(klass): relations = [] for rel in sqla_inspect(klass).relationships: if rel.direction.name == 'MANYTOONE': relations.append((rel.key, rel.mapper.class_.__name__)) return relations
def scan_attributes(klass): attributes = [] for column in sqla_inspect(klass).columns: attributes.append(column.key) return attributes
def to_dict(self): """ Serialize each SQLAlchemy model into a dict that captures a each model's name, attributes, types of attributes, and foreign keys. The dict will look like: { '<table name>': { 'class': <full module path to the model class>, 'table_name': <table name>, 'properties': [ { Output of _column_obj_to_dict }, ... ], 'foreign_keys': [ { 'attribute': <foreign key column name>, 'table': <foreign key table name> } ] } } """ orm_models_dict = {} self.logger.info('Serializing SQLAlchemy models to dicts') model_dict_template = { 'class': None, 'table_name': None, 'properties': [], 'foreign_keys': [] } for model_cls in self.imported_model_classes: model_dict = deepcopy(model_dict_template) model_name = model_cls.__name__ model_dict['table_name'] = model_cls.__tablename__ model_dict['class'] = model_name self.logger.info(f'Building model dict for {model_name} ...') # Inspect model columns and types for p in sqla_inspect(model_cls).iterate_properties: if not isinstance(p, ColumnProperty): continue if not hasattr(p, 'columns'): continue d = self._column_obj_to_dict(p.key, p.columns[0]) if d.pop('primary_key', None): model_dict['primary_key'] = p.key model_dict['properties'].append(d) fk = d.pop('foreign_key', {}) if fk: model_dict['foreign_keys'].append(fk) orm_models_dict[model_dict['table_name']] = model_dict return orm_models_dict
def _scan_relations(self, klass): relations = [] for rel in sqla_inspect(klass).relationships: if rel.direction.name == 'MANYTOONE': relations.append(rel.key) return relations