def sqlrelations_collect(self, source, target): """Finds all associations, prepares them and adds them to the corrsponding classes. """ detail = source.memberEnds[0] detailclass = detail.type master = source.memberEnds[1] masterclass = master.type if not masterclass.stereotype('sql:sql_content'): return if not detailclass.stereotype('sql:sql_content'): return mastertok = token(str(masterclass.uuid), True, outgoing_relations=[],incoming_relations=[]) detailtok = token(str(detailclass.uuid), True, outgoing_relations=[],incoming_relations=[]) if IAssociationClass.providedBy(source): detailtok.outgoing_relations.append(detail) mastertok.outgoing_relations.append(master) elif detail.aggregationkind in ['composite','aggregation','shared'] or \ IAssociationClass.providedBy(source): #for aggregations the arrow points from the master to the detail detailtok.incoming_relations.append(detail) mastertok.outgoing_relations.append(master) else: #simple FK relation, so other direction mastertok.incoming_relations.append(master)
def sqlrelations_foreignkeys(self, source, target): """Generate foreign key attributes. """ if source.stereotype('pyegg:stub'): return if not source.stereotype('sql:sql_content'): return targetclass = read_target_node(source, target.target) module=targetclass.parent # get the last attribute and append there the foreignkeys attrs = targetclass.attributes() attrnames = [att.targets[0] for att in attrs] incoming_relations = token(str(source.uuid), True, incoming_relations=[]).incoming_relations for relend in incoming_relations: klass = relend.type #no foreign keys needed for Association Classes if IAssociationClass.providedBy(relend.association): continue #fetch the opposite relation end if relend==relend.association.memberEnds[0]: otherend = relend.association.memberEnds[1] else: otherend = relend.association.memberEnds[0] otherclass = otherend.type nullable = not relend.aggregationkind=='composite' joins=calculate_joins(source, targetclass, otherclass, otherend.name, nullable = nullable) token(str(otherend.uuid), True, joins=joins)
def sqlrelations_relations(self, source, target): """generate relations. """ if source.stereotype('pyegg:stub'): return if not source.stereotype('sql:sql_content'): return targetclass = read_target_node(source, target.target) module = targetclass.parent directory = module.parent # get the last attribute and append there the relations attrs = targetclass.attributes() attrnames = [att.targets[0] for att in attrs] try: lastattr = targetclass.attributes()[-1] except IndexError: lastattr = None outgoing_relations = token(str(source.uuid), True, outgoing_relations=[]).outgoing_relations imps = Imports(module) if outgoing_relations: imps.set('sqlalchemy.orm', [['relationship', None]]) for relend in outgoing_relations: assoc = relend.association if relend==relend.association.memberEnds[0]: otherend = relend.association.memberEnds[1] else: otherend = relend.association.memberEnds[0] #Association classes are handled seperately #once we support association tables, we have to handle it here tgv = TaggedValues(assoc) if IAssociationClass.providedBy(assoc): # tgv = TaggedValues(otherend) We need to build support for tgvs on # member ends later klass = relend.type otherclass = relend.association relname = token(str(otherend.uuid), True, relname=otherend.name+'_associations').relname else: klass = relend.type otherclass = otherend.type relname = otherend.name if relname not in attrnames: attr = Attribute() attr.__name__ = str(attr.uuid) if lastattr: targetclass.insertafter(attr, lastattr) else: targetclass.insertfirst(attr) attr.targets = [relname] options = {} # collect options for relationship if otherend.aggregationkind == 'composite': options['cascade'] = "'all, delete-orphan'" if assoc.stereotype('sql:ordered'): order_by = tgv.direct('order_by', 'sql:ordered', None) if not order_by: msg = 'when setting a relation ordered you have to ' +\ 'specify order_by!' raise ValueError(msg) # if not prefixed, lets prefix it if not '.' in order_by: order_by = '%s.%s' % (otherclass.name, order_by) options['order_by'] = "'%s'" % order_by if assoc.stereotype('sql:attribute_mapped'): keyname = tgv.direct('key', 'sql:attribute_mapped', None) if not keyname: msg = 'when defining attribute_mapped you have to ' + \ 'specify a key' raise ValueError(msg) if assoc.stereotype('sql:ordered'): # support for ordered mapped collection # in this case we have to provide our own collection # see http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html, # secion 'Custom Dictionary-Based Collections' fname = 'orderedcollection.py' if fname not in directory: src = JinjaTemplate() src.template = templatepath(fname + '.jinja') src.params = {} directory[fname] = src # XXX: so that emptymoduleremoval doesnt kick the # template out better would be that jinjatemplates # dont get removed at all token('pymodules', True, modules=set()).modules.add(src) options['collection_class'] = \ "ordered_attribute_mapped_collection('%s')" % keyname imps.set('orderedcollection', [['ordered_attribute_mapped_collection', None]]) # unordered else: options['collection_class'] = \ "attribute_mapped_collection('%s')" % keyname imps.set('sqlalchemy.orm.collections', [['attribute_mapped_collection', None]]) # make the primaryjoin stmt if 1 or not IAssociationClass.providedBy(relend.association): tok = token(str(relend.uuid), True, joins=[]) if tok.joins: options['primaryjoin'] = "'%s'" % ','.join(tok.joins) # XXX: .navigable isn't yet correctly parsed from uml, thus the # hardcoding if True or relend.navigable: options['backref'] = "'%s'" % relend.name.lower() if assoc.stereotype('sql:lazy'): laziness = tgv.direct('laziness', 'sql:lazy', 'dynamic') options['lazy'] = "'%s'" % laziness #convert options into keyword params oparray = [] for k in options: oparray.append('%s = %s' % (k, options[k])) attr.value = "relationship('%s', %s)" % ( otherclass.name, ', '.join(oparray))