Thread: [Modeling-cvs] ProjectModeling/Modeling PyModel.py,NONE,1.1.2.1
Status: Abandoned
Brought to you by:
sbigaret
From: <sbi...@us...> - 2003-05-15 17:45:01
|
Update of /cvsroot/modeling/ProjectModeling/Modeling In directory sc8-pr-cvs1:/tmp/cvs-serv25871 Added Files: Tag: brch-0_9pre7-1-PyModel PyModel.py Log Message: Added PyModel.py and tests --- NEW FILE: PyModel.py --- #----------------------------------------------------------------------------- # # Modeling Framework: an Object-Relational Bridge for python # (c) 2001, 2002, 2003 Sebastien Bigaret # # This file is part of the Modeling Framework. # # The Modeling Framework is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as published # by the Free Software Foundation; either version 2 of the License, or (at # your option) any later version. # # The Modeling Framework is distributed in the hope that it will be # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License along # with the Modeling Framework; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #----------------------------------------------------------------------------- """ Pythonic Description of an OO-RDBM Model Rules: - a model is an object of type Model - a model may specify a list of entities, each of type Entity - an entity may specify a list of properties, that are either of type Attribute or \of type Relation - all references to attributes, relations, entities, models, is by their "name" i.e. not by their columnName or className, etc, as the case may be - whenever deemed useful, defaults are used - note that the default "type" for an attribute is int (not string -- seems more logical to me) - delete rule values are in lowercase New Changes to note: parentEntity -> parent Entity only exposes a properties[] (that holds all attributes and relations) default for Model.adaptorName is None added doc attribute on all description classes Previous changes: defaults class dictionary variable, for all model description classes deleteRule -> delete multiplicityLowerBound and miltiplicity upperBound -> multiplicity parentEntity -> isAlso default for displayLabel is value of name (not of columnName) Entity only has attributes[] (that may also be relations) """ ## import Modeling from Entity import externalNameForInternalName from utils import isListOrTuple identity=lambda x: x def debug(msg): print msg def error(msg): raise RuntimeError, msg class MP: def __init__(self, method, *args): self.method=method self.param=args class BaseDescription: ''' Base description class (should not be exported to client code) ''' VERSION='0.1' defaults = {} def update(self): pass ## NB pourrait servir a mettre une derniere fois a jour le component ## en fct. de self.__dict__ filtr'e par self.__class__.defaults.keys() ## ++ ?? peut-etre ajouter un truc du genre 'ignore' dans les defaults def build(self, model): pass def requiredField(self, field): ''' make test more useful ''' if not field: raise ValueError, 'Required field' return field def defaultedField(self, field, defaultValue): if field: return field else: return defaultValue def clone(self, descriptionObject): ''' return an independant copy of object ''' return descriptionObject def toXML(self): return '<later/>' #def __str__(self): # return '<%s instance at %s>'%(str(self.__class__), hex(id(self))) ## class Model(BaseDescription): ''' Describes a Model ''' defaults={ 'packageName': MP(identity, 'name'), 'adaptorName': '', 'connDict': { 'host': '', 'database': '', 'user': '', 'password': '' }, 'entities': [], 'associations': [], 'doc': '', } def __init__(self, name, **kw): self.name=self.requiredField(name) self.entities=list(kw.get('entities', [])) from Model import Model as MModel self.component=MModel(name) updateComponent(self, kw, Model.defaults, name, ignore=('entities','associations')) def entityNamed(self, name): for e in self.entities: if e.name==name: return e return None def validate(self): ''' validate this model instance ''' pass def generatePython(self,entityName=None): ''' generate code for model, or only for entityName if specified ''' pass def generateDBSchema(self,entityName=None): ''' Generate SQL only ''' pass def createDBSchema(self,entityName=None): ''' drops and recreates from scratch ''' pass def updateDBSchema(self,mode,entityName=None): ''' operate on existing tables (and defined by this schema), employing mode: - check: only report differences between model and existent DB - incremental: only add/modify columns in existing tables - clean: add/modify new columns/tables, and drop obsolete ones Note: renaming of columns or tables? ''' pass def addEntity(self, entityDescObj): ''' add entity to model ''' pass def removeEntity(self, name): ''' remove entity from model ''' pass def build_associations(self): """ """ debug('Processing associations') for assoc in self.associations: assoc.build(self) def build(self): # Check version if getattr(self, 'version', None)!=BaseDescription.VERSION: raise ValueError, 'Incompatible versions' # Build ordered list of entities self.ordered_entities=oe=[] entities=list(self.entities) entities_processed=[] for entity in [e for e in entities if not e.parent]: oe.append(entity) entities_processed.append(entity.name) entities.remove(entity) while entities: print [e.name for e in entities], entities_processed es=[e for e in entities if e.parent in entities_processed] for entity in es: entities.remove(entity) entities_processed.append(entity.name) oe.append(entity) # Build defaults, parent, attributes for entity in self.ordered_entities: entity.prebuild_with_defaults(model=self) entity.prebuild_with_parent_properties(model=self) # Build Associations self.build_associations() for entity in self.ordered_entities: # propagates changes made by Associations entity.prebuild_with_parent_properties(model=self) # Build attributes for entity in self.ordered_entities: entity.build_attributes(model=self) self.component.addEntity(entity.component) # Check inheritance for entity in self.ordered_entities: if entity.parent: superEntity=self.component.entityNamed(entity.parent) superEntity.addSubEntity(entity.component) # model / build phase 2 for entity in self.ordered_entities: entity.build_preprocess_relationships_and_inverse(self) for entity in self.ordered_entities: entity.build_toOne_relationships(self) for entity in self.ordered_entities: entity.build_toMany_relationships(self) ## class Entity(BaseDescription): ''' Describes an Entity ''' defaults={ 'className': MP(identity, 'name'), 'externalName': MP(externalNameForInternalName, 'name'), 'moduleName': MP(identity, 'name'), 'typeName': '', 'isAbstract': 0, 'isReadOnly': 0, 'properties': [], 'parent': '', 'doc': None, } def __init__(self, name, **kw): self.name=self.requiredField(name) #self.properties=kw.get('properties', ()) from Entity import Entity as MEntity self.component=MEntity(name) updateComponent(self, kw, Entity.defaults, name, ignore=('parent','properties')) def addAttribute(self, attributeDescObj, model): ''' add attribute to model ''' debug('Entity.addAttribute e: %s / attr: %s'%(self.name, attributeDescObj.name)) # check it does not already exist a=self.attributeNamed(attributeDescObj.name) if a: debug(' Found it -- ignoring / .entity()=%s'%a.component.entity()) return # continue self.properties.append(attributeDescObj) attributeDescObj.build(model, self) # bizarre, plutot build(entity, model) self.component.addAttribute(attributeDescObj.component) self.processAttributes(model) self.propagate_attribute(attributeDescObj, model) def attFKName(self, destEntityName): n='fk'+destEntityName count=1 while self.propertyNamed(n): n='fk'+destEntityName+str(count) count+=1 return n def attributeNamed(self, name): for prop in [r for r in self.properties if isinstance(r, Attribute)]: if prop.name==name: return prop return None def removeAttribute(self, attributeDescObj): ''' remove attribute from model ''' pass def addRelation(self, relationDescObj): ''' add relation to model ''' pass def attributes(self): "-" return [r for r in self.properties if isinstance(r, Attribute)] def propertyNamed(self, name): a=self.attributeNamed(name) if a: return a else: return self.relationshipNamed(name) def relationshipNamed(self, name): "-" for r in self.relationships(): if r.name==name: return r return None def relationships(self): "-" return [r for r in self.properties if isinstance(r, BaseRelation)] def removeRelation(self, relationDescObj): ''' remove relation from model ''' pass def inheritFrom(self, entityDescObj): ''' add to this entity all other atts & rels defined in entityDescObj ''' pass def primaryKey(self): """ Returns the entity's PK """ for prop in self.properties: if isinstance(prop, APrimaryKey): return prop return 0 def propagate_attribute(self, attributeDescObj, model): debug('Entity.propagate_attribute e: %s / attr: %s'%(self.name, attributeDescObj.name)) for e in [ee.name() for ee in self.component.subEntities()]: entity=model.entityNamed(e) #if not entity.component.attributeNamed(attributeDescObj.name): entity.addAttribute(attributeDescObj.clone(), model) def processAttributes(self, model): # Process attributes and look for PKs & attributes usedForLocking pks=[] usedForLocking=[] for prop in self.attributes(): prop.build(model, self) if isinstance(prop, APrimaryKey): pks.append(prop.component) if prop.usedForLocking: usedForLocking.append(prop.component) self.component.setPrimaryKeyAttributes(pks) self.component.setAttributesUsedForLocking(usedForLocking) def prebuild_with_defaults(self, model): "Phase 1" debug('Entity.build/1: %s'%self.name) # check defaults properties if Entity.defaults.get('properties'): # Update properties iff: # - they are not already present # - we do not add a PK if one is already defined self_prop_names=[prop.name for prop in self.properties] for dprop in Entity.defaults['properties']: if dprop.name not in self_prop_names: if not ( self.primaryKey() and isinstance(dprop, APrimaryKey) ): self.properties.append(dprop.clone()) def prebuild_with_parent_properties(self, model): """ Builds a Modeling.Entity / Phase 2 """ debug('Entity.build/2: %s'%self.name) # Check inheritance if self.parent: parent=model.entityNamed(self.parent) if not parent: raise ValueError, "Unable to find parent '%s' for entity '%s'"%(self.parent, self.name) # Update the properties self_prop_names=[prop.name for prop in self.properties] for pprop in parent.properties: if pprop.name in self_prop_names: # Overriden property, shouldnt be changed continue clone=pprop.clone() clone.propagated_by=self.parent self.properties.append(clone) def build_attributes(self, model): """ Builds a Modeling.Entity / Phase 3 """ debug('Entity.build/3: %s'%self.name) ## ATTRIBUTES # Add all attributes for prop in self.attributes(): prop.build(model, self) self.component.addAttribute(prop.component) debug('build_attributes %s.%s: %s'%(self.name,prop.name,prop.component.entity())) self.processAttributes(model) def build_preprocess_relationships_and_inverse(self, model): """ Builds a Modeling.Entity / Phase 4 """ if self.parent: return debug('Entity.build/4: %s'%self.name) for r in self.relationships(): destEntity=model.entityNamed(r.destination) if not destEntity: raise ValueError, 'Relation %s.%s: cannot find destination entity %s'%(self.name,r.name,r.destination) if getattr(r, 'inverse',None): self.forward_rel_info(r, model) def forward_rel_info(self, rel, model): ''' ''' debug('forward_rel_info %s.%s'%(self.name,rel.name)) def check_or_make_equal(x,y): if x and y: assert x == y return x and (x,x) or (y,y) for e in self.component.allSubEntities(): r=model.entityNamed(e.name()).relationshipNamed(rel.name) r.inverse='' rel.src,r.src=check_or_make_equal(rel.src,r.src) rel.dst,r.dst=check_or_make_equal(rel.dst,r.dst) if rel.inverse: invr=model.entityNamed(rel.destination).relationshipNamed(rel.inverse) if invr.inverse: assert invr.inverse==rel.name else: invr.inverse=rel.name rel.src,invr.dst=check_or_make_equal(rel.src,invr.dst) rel.dst,invr.src=check_or_make_equal(rel.dst,invr.src) def build_toOne_relationships(self, model): toOneRels=[r for r in self.properties if isinstance(r, RToOne)] for prop in toOneRels: # Note: we build() AFTER we add the relationship, or # relationship.component.entity==None and then it's impossible to # add a join to such a MRelationship self.component.addRelationship(prop.component) prop.build(model, self) def build_toMany_relationships(self, model): toManyRels=[r for r in self.properties if isinstance(r, RToMany)] for prop in toManyRels: # Note: we build() AFTER we add the relationship, or # relationship.component.entity==None and then it's impossible to # add a join to such a MRelationship self.component.addRelationship(prop.component) prop.build(model, self) def _void_void(self, model): # We need to have two phases: building relationships requires that all # attributes of all entities are previously initalized and built. ## RELATIONS toOneRels=[r for r in self.properties if isinstance(r, RToOne)] toManyRels={} [toManyRels.setdefault(k,v) for k,v in [(r.name,r) for r in self.properties if isinstance(r, RToMany)]] # process inverse #for r in toOneRels+toManyRels ## TBD: ici il faut encore 2 phases: 1. traiter toutes les toOnes, ## puis 2. toutes les toManys, ou alors 1. traiter les inverses, 2. traiter ## les relations. ## La 2eme solution implique plus de tests dans le build des relations ## (si inverse mais pas encore traitee, chercher/creer la fk et sinon ## recuperer la bonne avec le bon nom... # toOne #print toManyRels for prop in toOneRels: # Note: we build() AFTER we add the relationship, or # relationship.component.entity==None and then it's impossible to # add a join to such a MRelationship self.component.addRelationship(prop.component) prop.build(model, self) # toMany for prop in toManyRels.values(): self.component.addRelationship(prop.component) prop.build(model, self) ## TBD: RETURN les entites modifiees pour update et propagation! ## ex. updateAfterChanges() ## pourrait tester l'existant avec entity.propertyNamed ## class Attribute(BaseDescription): ''' Describes an Attribute ''' defaults={ 'columnName': MP(externalNameForInternalName, 'name'), #'key': 0, 'usedForLocking': 0, 'type': 'int', 'externalType': 'INTEGER', 'isClassProperty': 1, 'isRequired': 0, 'precision': 0, 'width': 0, 'scale': 0, 'defaultValue': None, 'displayLabel': '', 'doc': None, } def __init__(self, name, **kw): self.name=self.requiredField(name) from Attribute import Attribute as MAttribute self.component=MAttribute(name) updateComponent(self, kw, Attribute.defaults, name, ignore='usedForLocking') def build(self, model, entity): pass def clone(self): d=self.__dict__.copy() try: del d['component'] except: pass clone=apply(self.__class__, (), d) return clone class AInteger(Attribute): defaults={ 'type': 'int', 'externalType': 'INTEGER', 'defaultValue': 0, } def __init__(self, name, **kw): apply(Attribute.__init__.im_func, (self, name), kw) updateComponent(self, kw, AInteger.defaults, name, ignore='usedForLocking') class AString(Attribute): defaults={ 'type': 'string', 'externalType': 'VARCHAR', 'width': 255, 'defaultValue': '', } def __init__(self, name, **kw): apply(Attribute.__init__.im_func, (self, name), kw) updateComponent(self, kw, AString.defaults, name, ignore='usedForLocking') # http://www.onlamp.com/pub/a/onlamp/2001/09/13/aboutSQL.html # # SQL Wisdom #7) The data type is invariably different -- even if it has the # same name -- in another database. Always check the documentation. class AFloat(Attribute): defaults={ 'type': 'float', 'externalType': 'NUMERIC', 'precision': 15, 'scale': 5, 'defaultValue': 0.0, } def __init__(self, name, **kw): apply(Attribute.__init__.im_func, (self, name), kw) updateComponent(self, kw, AFloat.defaults, name, ignore='usedForLocking') class ADateTime(Attribute): defaults={ 'type': 'DateTime', 'externalType': 'TIMESTAMP', 'defaultValue': None, } def __init__(self, name, **kw): apply(Attribute.__init__.im_func, (self, name), kw) updateComponent(self, kw, ADateTime.defaults, name, ignore='usedForLocking') class APrimaryKey(AInteger): defaults={ 'isClassProperty': 0, 'isRequired': 1, 'defaultValue': MP(lambda i: (i and [0] or [None])[0], 'isClassProperty'), 'doc': 'Primary Key', } def __init__(self, name='id', **kw): apply(AInteger.__init__.im_func, (self, name), kw) updateComponent(self, kw, APrimaryKey.defaults, name, ignore='usedForLocking') class AForeignKey(AInteger): defaults={ 'isClassProperty': 0, 'isRequired': 0, 'defaultValue': None, 'doc': 'Foreign Key', } def __init__(self, name='id', **kw): apply(AInteger.__init__.im_func, (self, name), kw) updateComponent(self, kw, AForeignKey.defaults, name, ignore='usedForLocking') ## def xor(a,b): return not not ((a and not b) or (not a and b)) class BaseRelation(BaseDescription): ''' Describes a Relation ''' defaults={ 'delete': 0, #'key': 0, #'destinationEntity': '', ##'usedForLocking': 0, 'isClassProperty': 1, 'multiplicity': [0,1], 'joinSemantic': 0, 'src': '', 'dst': '', 'displayLabel': '', 'doc': '', 'inverse': '', } # Additinal attr. for self.: src_dst_specified def __init__(self, name, destination, **kw): self.name=self.requiredField(name) self.destination=self.requiredField(destination) from Relationship import SimpleRelationship as MRelationship self.component=MRelationship(name) updateComponent(self,kw,BaseRelation.defaults,name, ignore=('src', 'dst', 'multiplicity', 'src_dst_specified', 'inverse')) if xor(self.src, self.dst): raise ValueError, "Parameters src and dst should be both specified" def clone(self): d=self.__dict__.copy() try: del d['component'] # we do not want to copy this except: pass try: del d['src_dst_specified'] except: pass clone=apply(self.__class__, (), d) return clone class RToOne(BaseRelation): defaults={ 'multiplicity': [0,1], 'joinSemantic': 0, } propagated_by=None def __init__(self, name, destination, **kw): apply(BaseRelation.__init__.im_func, (self, name, destination), kw) updateComponent(self, kw, RToOne.defaults, name, ignore=('src', 'dst', 'multiplicity', 'src_dst_specified', 'inverse')) def build(self, model, entity): debug('(build toOne) Relation %s.%s'%(entity.name,self.name)) self.src_dst_specified=not not self.src destEntity=model.entityNamed(self.destination) destMEntity=destEntity.component #print model.component.entitiesNames() # Check multiplicity lowB=int(self.multiplicity[0]) uppB=int(self.multiplicity[1]) err='' if lowB not in (0,1): err+='Lower bound must be 0 or 1. ' if uppB not in (0,1): err+='Upper bound must be 0 or 1. ' if err: raise ValueError, 'Relation %s.%s: %s'%(entity.component.name(), self.component.name(), err) self.component.setMultiplicity(lowB, uppB) # Process src & dst attributes if not self.src_dst_specified: destinationMAttribute=destMEntity.primaryKeyAttributes()[0] if not self.propagated_by: sourceAttribute=AForeignKey(entity.attFKName(destEntity.name)) else: # The attribute has already been propagated, find & re-use it mparent=model.entityNamed(self.propagated_by).component mparent_destAttr=mparent.relationshipNamed(self.name).sourceAttributes() fkName=mparent_destAttr[0].name() sourceAttribute=entity.attributeNamed(fkName) if not sourceAttribute: error('Could not find src.attr. %s.%s for relationship %s.%s with %s inheriting from parent=%s (propagated_by=%s).\nThis should not happen, please report to <mod...@li...> along with your model'%(entity.name,fkName,entity.name,self.name,entity.name,entity.parent, self.propagated_by)) entity.addAttribute(sourceAttribute, model) sourceMAttribute=sourceAttribute.component self.src=sourceAttribute.name self.dst=destinationMAttribute.name() entity.forward_rel_info(self, model) else: # src & dst specified sourceMAttribute=entity.component.attributeNamed(self.src) if not sourceMAttribute: raise ValueError, 'Relation %s.%s: unable to find source attribute %s.%s'%(entity.component.name(), self.component.name(), entity.name, self.src) destinationMAttribute=destMEntity.attributeNamed(self.dst) # check destinationAttribute if not destinationMAttribute: if self.dst: err_msg='Relation %s.%s: unable to find destination attribute %s.%s'%(entity.name, self.name, self.destination, self.dst) else: err_msg='Relation %s.%s: unable to find destination attribute: no primary key declared for entity %s'%(entity.name, self.name, self.destination) raise ValueError, err_msg # src and dest are set, ok from Join import Join as MJoin j=MJoin(sourceMAttribute, destinationMAttribute) self.component.addJoin(j) #if not self.component.destinationEntity(): import pdb ; pdb.set_trace() class RToMany(BaseRelation): defaults={ 'multiplicity': [0,None], 'joinSemantic': 0, } propagated_by=None def __init__(self, name, destination, **kw): apply(BaseRelation.__init__.im_func, (self, name, destination), kw) updateComponent(self, kw, RToMany.defaults, name, ignore=('src', 'dst', 'multiplicity','inverse')) def build(self, model, entity): self.src_dst_specified=not not self.src debug('(build toMany) Relation %s.%s(%s->%s)'%(entity.name,self.name, self.src, self.dst)) #print model.component.entitiesNames() destEntity=model.entityNamed(self.destination) destMEntity=destEntity.component # Check multiplicity lowB=int(self.multiplicity[0]) uppB=self.multiplicity[1] err='' if uppB in (0,1): err+='Invalid value for upper bound of a toMany rel.' if err: raise ValueError, 'Relation %s.%s: %s'%(entity.component.name(), self.component.name(), err) self.component.setMultiplicity(lowB, uppB) # Process src & dst attributes if not self.src_dst_specified: sourceMAttribute=entity.component.primaryKeyAttributes()[0] if not self.propagated_by: destinationAttribute=AForeignKey(destEntity.attFKName(entity.name)) else: # The attribute has already been propagated, find it mparent=model.entityNamed(self.propagated_by).component mparent_destAttr=mparent.relationshipNamed(self.name).destinationAttributes() fkName=mparent_destAttr[0].name() destinationAttribute=destEntity.attributeNamed(fkName) if not destinationAttribute: error('Could not find dest.attr. %s.%s for relationship %s.%s with %s inheriting from parent=%s (propagated_by=%s).\nThis should not happen, please report to <mod...@li...> along with your model'%(destEntity.name,fkName,entity.name,self.name,entity.name,entity.parent, self.propagated_by)) destEntity.addAttribute(destinationAttribute, model) destinationMAttribute=destinationAttribute.component self.src=sourceMAttribute.name() self.dst=destinationMAttribute.name() entity.forward_rel_info(self, model) else: # src and dst specified sourceMAttribute=entity.component.attributeNamed(self.src) destinationMAttribute=destMEntity.attributeNamed(self.dst) if not destinationMAttribute: raise ValueError, 'Relation %s.%s: unable to find destination attribute %s.%s'%(entity.component.name(), self.component.name(), self.destination, self.dst) # Check source attribute if not sourceMAttribute: if self.src: err_msg='Relation %s.%s: unable to find source attribute %s.%s'%(entity.name, self.name, entity.name, self.src) else: err_msg='Relation %s.%s: unable to find source attribute: no primary key declared for entity %s'%(entity.name, self.name, entity.name) raise ValueError, err_msg # src and dest are set from Join import Join as MJoin j=MJoin(sourceMAttribute, destinationMAttribute) self.component.addJoin(j) #if not self.component.destinationEntity(): import pdb ; pdb.set_trace() class Association(BaseDescription): ''' Describes a Relation between 2 entities ''' # TBD a placer dans les prop. de Entity # ex. Relation('Employee', 'Address', ([0,None],cascade), ([0,1],deny)) # --> soit ca # --> soit tout a la main # --> soit dans les Entity, RToOne, RToMany: # - avec seulement destEntity # - avec destEntity et inverse= def __init__(self, src, dst, multiplicity=[0,1], invMultiplicity=[0,None], srcProps={}, dstProps={}): if multiplicity[1]>1: raise ValueError, 'invalid mult' if not (invMultiplicity[1] in (None, '*', -1) or invMultiplicity[0]>1): raise ValueError, 'invalid invMult' self.src=src self.dst=dst ## TBD 1: inverser src/dst si multiplicities sont inversees ## TBD 2: possibilite d'ajout des srcKey/dstKey self.multiplicity=multiplicity self.invMultiplicity=invMultiplicity self.srcProps=srcProps self.dstProps=dstProps def build(self, model): srcE=model.entityNamed(self.src) if not srcE: raise ValueError, 'Association: Unable to find source entity %s'%self.src dstE=model.entityNamed(self.dst) if not dstE: raise ValueError, 'Association: Unable to find destination entity %s'%self.dst dstAtt=dstE.primaryKey() if not dstAtt: dstAtt=APrimaryKey() dstE.properties.append(srcAtt) # FK: create it (TBD: unless ajout dstKey cf __init__) srcAtt=AForeignKey(srcE.attFKName(dstE.name)) srcE.properties.append(srcAtt) # Relationships srcRelName='to'+dstE.name # TBD: build it appropriately! dstRelName='to'+srcE.name+'s' # TBD: build it appropriately! srcRel=RToOne(srcRelName, dstE.name, ## TBD: +properties src=srcAtt.name, dst=dstAtt.name) dstRel=RToMany(dstRelName, srcE.name, ## TBD: +properties src=dstAtt.name, dst=srcAtt.name) srcE.properties.append(srcRel) dstE.properties.append(dstRel) ## def updateComponent(object, kw, defaults, name, ignore=()): """ Parameters: component -- kw -- NB: kw is not modified defaults -- name -- """ def copy(v): if isListOrTuple(v): return list(v) #return list(v) if type(v) is type({}): return v.copy() #if hasattr(v, 'clone') and callable(v.clone): # print '###Cloning' # return v.clone() # props else: return v if type(ignore) is type(''): ignore=[ignore] ignore=list(ignore) ; ignore.extend(['name','propagated_by']) _kw=defaults.copy() # We need to clone the defaults['properties'], such as in Entity.defaults: # when this is not done, Entities.default is reused as is in # different entities; e.g. each default attr. is assigned # to different entities, which in turn manipulate and change that attr. # and its component --> a test like in # test_PyModel.checkEntitiesProperties() then fails. clone_or_self=lambda p: (hasattr(p, 'clone') and callable(p.clone)) and p.clone() or p if _kw.get('properties'): _kw['properties']=map(clone_or_self, _kw['properties']) _kw.update(kw) ; kw=_kw kw['name']=name for k,v in kw.items(): # On ajoute tout a l'objet aussi, ca sert pdt le build setattr(object,k,copy(v)) if k in ignore: continue if isinstance(v,MP): l=[kw[n] for n in v.param] v=apply(v.method, l) else: v=copy(v) #try: print k, ': ', v #except: import pdb ; pdb.set_trace() object.component.takeStoredValueForKey(v,k) ## |