def generate_table_schema_interface(ti): '''!+DO_NOT_REORDER_USER_APPLIED_INTERFACES def get_domain_interfaces(domain_model): """Return the domain bases for an interface as well as a filtered implements only list (base interfaces removed). Note that for 2nd level (mapped) domain classes i.e. those that inherit from another domain class e.g. Event(Doc), Office(Group), OfficeMember(GroupMembership), an IIModelInterface-providing I*TableSchema interface had already been created (for base class) and assigned to the super class--and that interface will match as one of the domain_base interfaces here. """ domain_bases = [] domain_implements = [] for iface in interface.implementedBy(domain_model): if IIModelInterface.providedBy(iface): domain_bases.append(iface) else: domain_implements.append(iface) domain_bases = tuple(domain_bases) or (IAlchemistContent,) return domain_bases, domain_implements bases, implements = get_domain_interfaces(ti.domain_model) ''' # derived_table_schema: # - ALWAYS dynamically generated # - directlyProvides IIModelInterface (by virtue of IAlchemistContent) type_key = naming.polymorphic_identity(ti.domain_model) # use the class's mapper select table as input for the transformation table_schema_interface_name = naming.table_schema_interface_name(type_key) domain_table = utils.get_local_table(ti.domain_model) derived_table_schema = transmute( domain_table, annotation=ti.descriptor_model, interface_name=table_schema_interface_name, __module__=INTERFACE_MODULE.__name__, #_generated_by="bungeni.alchemist.catalyst.generate_table_schema_interface" #bases=bases) bases=(IAlchemistContent, )) # apply, register on type_info, set on module interface.classImplements(ti.domain_model, derived_table_schema) utils.inisetattr(ti, "derived_table_schema", derived_table_schema) setattr(INTERFACE_MODULE, table_schema_interface_name, derived_table_schema) log.info("generate_table_schema_interface: %s", derived_table_schema) # defensive sanity check - that derived_table_schema is precisely the FIRST # resolving IIModelInterface-providing interface implemented by domain_model # !+ this failing does not necessarily mean an incorrectness for iface in interface.implementedBy(ti.domain_model): if IIModelInterface.providedBy(iface): assert iface is derived_table_schema, (ti.domain_model, iface, id(iface), derived_table_schema, id(derived_table_schema)) break '''!+DO_NOT_REORDER_USER_APPLIED_INTERFACES
def new_custom_domain_model(type_key, domain_interface, archetype_key): domain_model_name = naming.model_name(type_key) assert archetype_key, \ "Custom descriptor %r does not specify an archetype" % (type_key) archetype = getattr(MODEL_MODULE, naming.model_name(archetype_key)) # AttributeError # !+ assert archetype constraints domain_model = type(domain_model_name, (archetype,), { "__module__": MODEL_MODULE.__name__, "extended_properties": [], } ) # apply domain_interface classImplements(domain_model, domain_interface) # set on MODEL_MODULE (register on type_info downstream) setattr(MODEL_MODULE, domain_model_name, domain_model) # db map custom domain class from sqlalchemy.orm import mapper mapper(domain_model, inherits=archetype, polymorphic_on=utils.get_local_table(archetype).c.type, polymorphic_identity=type_key, #naming.polymorphic_identity(domain_model), ) log.info("new_custom_domain_model [%s] %s.%s" % ( type_key, MODEL_MODULE.__name__, domain_model_name)) return domain_model
def new_custom_domain_model(type_key, domain_interface, archetype_key): domain_model_name = naming.model_name(type_key) assert archetype_key, \ "Custom descriptor %r does not specify an archetype" % (type_key) archetype = getattr(MODEL_MODULE, naming.model_name(archetype_key)) # AttributeError # !+ assert archetype constraints domain_model = type(domain_model_name, (archetype, ), { "__module__": MODEL_MODULE.__name__, "extended_properties": [], }) # apply domain_interface classImplements(domain_model, domain_interface) # set on MODEL_MODULE (register on type_info downstream) setattr(MODEL_MODULE, domain_model_name, domain_model) # db map custom domain class from sqlalchemy.orm import mapper mapper( domain_model, inherits=archetype, polymorphic_on=utils.get_local_table(archetype).c.type, polymorphic_identity= type_key, #naming.polymorphic_identity(domain_model), ) log.info("new_custom_domain_model [%s] %s.%s" % (type_key, MODEL_MODULE.__name__, domain_model_name)) return domain_model
def generate_table_schema_interface(ti): '''!+DO_NOT_REORDER_USER_APPLIED_INTERFACES def get_domain_interfaces(domain_model): """Return the domain bases for an interface as well as a filtered implements only list (base interfaces removed). Note that for 2nd level (mapped) domain classes i.e. those that inherit from another domain class e.g. Event(Doc), Office(Group), OfficeMember(GroupMembership), an IIModelInterface-providing I*TableSchema interface had already been created (for base class) and assigned to the super class--and that interface will match as one of the domain_base interfaces here. """ domain_bases = [] domain_implements = [] for iface in interface.implementedBy(domain_model): if IIModelInterface.providedBy(iface): domain_bases.append(iface) else: domain_implements.append(iface) domain_bases = tuple(domain_bases) or (IAlchemistContent,) return domain_bases, domain_implements bases, implements = get_domain_interfaces(ti.domain_model) ''' # derived_table_schema: # - ALWAYS dynamically generated # - directlyProvides IIModelInterface (by virtue of IAlchemistContent) type_key = naming.polymorphic_identity(ti.domain_model) # use the class's mapper select table as input for the transformation table_schema_interface_name = naming.table_schema_interface_name(type_key) domain_table = utils.get_local_table(ti.domain_model) derived_table_schema = transmute( domain_table, annotation=ti.descriptor_model, interface_name=table_schema_interface_name, __module__=INTERFACE_MODULE.__name__, #_generated_by="bungeni.alchemist.catalyst.generate_table_schema_interface" #bases=bases) bases=(IAlchemistContent,)) # apply, register on type_info, set on module interface.classImplements(ti.domain_model, derived_table_schema) utils.inisetattr(ti, "derived_table_schema", derived_table_schema) setattr(INTERFACE_MODULE, table_schema_interface_name, derived_table_schema) log.info("generate_table_schema_interface: %s", derived_table_schema) # defensive sanity check - that derived_table_schema is precisely the FIRST # resolving IIModelInterface-providing interface implemented by domain_model # !+ this failing does not necessarily mean an incorrectness for iface in interface.implementedBy(ti.domain_model): if IIModelInterface.providedBy(iface): assert iface is derived_table_schema, (ti.domain_model, iface, id(iface), derived_table_schema, id(derived_table_schema)) break '''!+DO_NOT_REORDER_USER_APPLIED_INTERFACES
def instrument_extended_properties(cls, object_type=None, from_class=None): if object_type is None: object_type = utils.get_local_table(cls).name if from_class is None: from_class = cls # ensure cls.__dict__.extended_properties cls.extended_properties = cls.extended_properties[:] for vp_name, vp_type in from_class.extended_properties: if (vp_name, vp_type) not in cls.extended_properties: cls.extended_properties.append((vp_name, vp_type)) setattr(cls, vp_name, vertical_property(object_type, vp_name, vp_type)) mapper_add_relation_vertical_property(cls, vp_name, vp_type)
def instrument_extended_properties(cls, object_type=None): if object_type is None: object_type = utils.get_local_table(cls).name for vp_name, vp_type in cls.extended_properties: setattr(cls, vp_name, vertical_property(object_type, vp_name, vp_type)) mapper_add_relation_vertical_property(cls, vp_name, vp_type)
def decorate_model(self, model): # Assumption: if a domain class is explicitly pre-defined, then it is # assumed that all necessary setup is also taken care of. # Typically, only the sub-classes of an archetype (mapped to a same # table) need dynamic creation/setup. def get_audit_class_for(auditable_class): audit_cls_name = "%sAudit" % (auditable_class.__name__) return getattr(MODEL_MODULE, audit_cls_name, None) def get_base_audit_class(model): """Identify what should be the BASE audit class for a {model}Audit class to inherit from, and return it. """ assert interfaces.IFeatureAudit.implementedBy(model), model ti = capi.get_type_info(model) if ti.archetype is None: # !+ should this be allowed to ever happen? # i.e. require each type to declare an archetype? base_audit_class = domain.Audit else: base_audit_class = get_audit_class_for(ti.archetype) if base_audit_class is None: # fallback to get the audit class for the sys archetype base_audit_class = get_audit_class_for(ti.sys_archetype) assert base_audit_class is not None, (model, ti.archetype, base_audit_class) return base_audit_class def new_audit_class(model): """Create, set on MODEL_MODULE, and map {model}Audit class. """ base_audit_cls = get_base_audit_class(model) audit_cls = base_audit_cls.auditFactory(model) # set on MODEL_MODULE setattr(MODEL_MODULE, audit_cls.__name__, audit_cls) # mapper for newly created audit_cls mapper(audit_cls, inherits=base_audit_cls, polymorphic_identity=naming.polymorphic_identity(model)) log.info("GENERATED new_audit_class %s(%s) for type %s", audit_cls, base_audit_cls, model) return audit_cls # domain - audit class audit_cls = get_audit_class_for(model) if audit_cls is None: audit_cls = new_audit_class(model) # auditor - head cls import bungeni.core.audit bungeni.core.audit.set_auditor(model) # mapper - audit class # assumption: audit_cls uses single inheritance only (and not only for # those created dynamically in feature_audit()) base_audit_cls = audit_cls.__bases__[0] assert issubclass(base_audit_cls, domain.Audit), \ "Audit class %s is not a proper subclass of %s" % ( audit_cls, domain.Audit) # extended attributes - propagate any on head cls also to its audit_cls for vp_name, vp_type in model.extended_properties: mapper_add_relation_vertical_property(audit_cls, vp_name, vp_type) # !+NOTE: capi.get_type_info(model).descriptor_model is still None # model.changes <-> change.audit.audit_head=doc: # doc[@TYPE] <-- TYPE_audit <-> audit <-> change # get head table for kls, and its audit table. tbl = utils.get_local_table(model) # NOT mapped_table, as when cls_mapper.single=False (e.g. for # the case of the group type) it gves an sa.sql.expression.Join, # and not a table object: # principal JOIN "group" ON principal.principal_id = "group".group_id audit_tbl = getattr(schema, naming.audit_table_name(tbl.name)) cls_mapper = class_mapper(model) cls_mapper.add_property( "changes", relation( domain.Change, # join condition, may be determined by a composite primary key primaryjoin=sa.and_(*[ pk_col == audit_tbl.c.get(pk_col.name) for pk_col in tbl.primary_key ]), secondary=audit_tbl, secondaryjoin=sa.and_( audit_tbl.c.audit_id == schema.change.c.audit_id, ), lazy=True, order_by=schema.change.c.audit_id.desc(), cascade="all", passive_deletes=False, # SA default ))
def decorate_model(self, model): # Assumption: if a domain class is explicitly pre-defined, then it is # assumed that all necessary setup is also taken care of. # Typically, only the sub-classes of an archetype (mapped to a same # table) need dynamic creation/setup. def get_audit_class_for(auditable_class): audit_cls_name = "%sAudit" % (auditable_class.__name__) return getattr(MODEL_MODULE, audit_cls_name, None) def get_base_audit_class(model): """Identify what should be the BASE audit class for a {model}Audit class to inherit from, and return it. """ assert interfaces.IFeatureAudit.implementedBy(model), model ti = capi.get_type_info(model) if ti.archetype is None: # !+ should this be allowed to ever happen? # i.e. require each type to declare an archetype? base_audit_class = domain.Audit else: base_audit_class = get_audit_class_for(ti.archetype) if base_audit_class is None: # fallback to get the audit class for the sys archetype base_audit_class = get_audit_class_for(ti.sys_archetype) assert base_audit_class is not None, (model, ti.archetype, base_audit_class) return base_audit_class def new_audit_class(model): """Create, set on MODEL_MODULE, and map {model}Audit class. """ base_audit_cls = get_base_audit_class(model) audit_cls = base_audit_cls.auditFactory(model) # set on MODEL_MODULE setattr(MODEL_MODULE, audit_cls.__name__, audit_cls) # mapper for newly created audit_cls mapper(audit_cls, inherits=base_audit_cls, polymorphic_identity=naming.polymorphic_identity(model) ) log.info("GENERATED new_audit_class %s(%s) for type %s", audit_cls, base_audit_cls, model) return audit_cls # domain - audit class audit_cls = get_audit_class_for(model) if audit_cls is None: audit_cls = new_audit_class(model) # auditor - head cls import bungeni.core.audit bungeni.core.audit.set_auditor(model) # mapper - audit class # assumption: audit_cls uses single inheritance only (and not only for # those created dynamically in feature_audit()) base_audit_cls = audit_cls.__bases__[0] assert issubclass(base_audit_cls, domain.Audit), \ "Audit class %s is not a proper subclass of %s" % ( audit_cls, domain.Audit) # extended attributes - propagate any on head cls also to its audit_cls for vp_name, vp_type in model.extended_properties: mapper_add_relation_vertical_property( audit_cls, vp_name, vp_type) # !+NOTE: capi.get_type_info(model).descriptor_model is still None # model.changes <-> change.audit.audit_head=doc: # doc[@TYPE] <-- TYPE_audit <-> audit <-> change # get head table for kls, and its audit table. tbl = utils.get_local_table(model) # NOT mapped_table, as when cls_mapper.single=False (e.g. for # the case of the group type) it gves an sa.sql.expression.Join, # and not a table object: # principal JOIN "group" ON principal.principal_id = "group".group_id audit_tbl = getattr(schema, naming.audit_table_name(tbl.name)) cls_mapper = class_mapper(model) cls_mapper.add_property("changes", relation(domain.Change, # join condition, may be determined by a composite primary key primaryjoin=sa.and_( *[ pk_col == audit_tbl.c.get(pk_col.name) for pk_col in tbl.primary_key ]), secondary=audit_tbl, secondaryjoin=sa.and_( audit_tbl.c.audit_id == schema.change.c.audit_id, ), lazy=True, order_by=schema.change.c.audit_id.desc(), cascade="all", passive_deletes=False, # SA default ))