Re: [Modeling-users] Pythonic, and non-XML, Model description
Status: Abandoned
Brought to you by:
sbigaret
From: Sebastien B. <sbi...@us...> - 2003-05-15 18:49:52
|
Hi there, More comments about PyModels --we need it in release 0.9 for the 1 page tutorial to be really efficient. I started to implement things, I needed to play with some pymodels to make up my mind on some specific stuff (esp. on declaring relationships). Obviously the starting point was made of version 4 of PyModel and sample_PyModel, I must admit that having these two modules made it possible to code a first sketch for PyModel quite quickly --thanks all for your comments. NB:For the curious that want to play with it to, I've committed a first implementation in CVS branch brch-0_9pre7-1-PyModel (directory Modeling/, where PyModel.py and tests/test_PyModel.py are added, plus the following modules that were slightly changed: Model, Entity, Attribute and Relationships. Obviously you do NOT need to check this out to participate to the discussion! I expose here the points that were raised when trying it: - model & entity declarations: nothing has changed, except that I added the possibility to declare 'associations' in an entity (see below) - defaults:=20 i. the way calculated defaults are declared needed to be changed, example: model.defaults=3D{ 'packageName': MP(identity, 'name'), } where MP is a special class for this (MP: Method/Parameters) identity is the method to be called (here identity=3Dlambda x:x) 'name' is the name of the object's (model's) attribute to pass as an attribute (it should be an object's attribute) When multiple parameters are needed: MP(function,'param1','param2') ii. sample_PyModel_4 said: Entity('Employee', properties=3D[ # [...] AForeignKey(Attribute.fk('Store'),=20 isClassProperty=3D0), ] ) --> Attribute.fk('<destEntity>') was not a good idea, it's even not feasible. So: either you declare (and name) the foreign key, or you rely on the initialization of a relationship to create it. =20=20=20=20=20=20=20=20=20=20 - attributes: nothing changed - relationships: there are two different ways to declare them: a. fully qualified rels i.e. when source and destination key are specified such as in: Entity('Employee',=20 AForeignKey('fkStore'), properties=3D[RToOne('toStore','Store',src=3D'fkStore',dst=3D'= id')] ) =20=20=20=20=20=20=20=20 b. unqualified rels, such as in: Entity('Employee', properties=3D[ RToOne('toStore', 'Store') ] ) When using such a scheme, you need to identify the inverse relationship, if any, by the keyword 'inverse'; it can be specified in either relationships, or in both. Example: Entity('Employee', properties=3D[ RToOne('toStore', 'Store',=20 inverse=3D'toEmployees') ] ) Both a. and b. are implemented.=20 In case b. the foreign key is created in the source (resp. destination) entity for a RToOne (resp. RToMany) relationship Note: the default for trelationships, such as in: RToOne.defaults['sourceAttribute'] =3D RToOne.attNameFromRel #'fk'+destEnt+= destAtt+count is not implemented but it will. For the moment being it's hard-coded. - new object: Association Association objects make it easier to quickly design a relationship and its inverse. Proposed usage: model.entities =3D [ Entity('Employee'), Entity('SalesClerk', parent=3D'Employee'), Entity('Mark'), Entity('Store'), ] model.associations =3D [ Association('Employee', 'Store'), Association('Mark', 'Employee'), ] (this is the StoreEmployee test model without the attributes) Another full usage could be: # [...] model.associations =3D [ Association('Employee: toStore', 'Store: toEmployees', [0,1], # Employee -----> Store [0,None], # Store ---->> Employee ) ] (Implementation note: for the moment Association('E1', 'E2') builds a toOne E1--->E2 and a toMany E2--->E1) =3D> this is where I'm not clear at all. It seems to me that we should be able to specify: - the source and destination entity (mandatory) - the multiplicity for both relationships (optional) - the names of the toOne rel. and of the toMany rel. (optional) - maybe extra parameters for each rels, something like: model.associations =3D [ Association('Employee: toStore', 'Store: toEmployees', [0,1], # Employee -----> Store [0,None], # Store ---->> Employee { 'doc': 'toOne: Employee to Store' }, # Emp.toStore { 'doc': 'toMany: Store to Employee' }, # Store.toEmp. ) ] I'm not sure this looks pretty, so if you have ideas they are welcome ;) Now for the details: * multiplicity: unconstrained upper bound can be -1, '*' or None * I choose 'required' instead of 'allowsNone' for attributes/rels. Enclosed is a (working) version of sample_PyModel_5.py As usual, your comments and critics are appreciated. A PyModel *will* be integrated in 0.9, I'd like it to meet *your* needs ;) Cheers, -- S=E9bastien. ------------------------------------------------------------------------ ''' sample_PyModel_5.py A sample Pythonic OO-RDB Model=20 (re-expressing testPackages/StoreEmployees/model_StoreEmployees.xml) -- A PyModel must define a global variable called 'model', that is of type Mod= el ''' from PyModel import * ## # Set preferred defaults for this model (when different from=20 # standard defaults, or if we want to make things explicit) AFloat.defaults['precision'] =3D 10 AFloat.defaults['scale'] =3D 10 AString.defaults['width'] =3D 20 RToOne.defaults['delete'] =3D 2 #'cascade' RToOne.defaults['multiplicity'] =3D [0,1]=20 #RToOne.defaults['sourceAttribute'] =3D RToOne.attNameFromRel # 'fk'+destEn= t+destAtt+count #RToOne.defaults['destinationAttribute'] =3D 'id' RToMany.defaults['delete'] =3D 1 # 'deny' # this is temporary, final version # will say 'deny'/'cascade'/'nullif= y' # as expected RToMany.defaults['multiplicity'] =3D [0,None] #RToMany.defaults['sourceAttribute'] =3D 'id' #RToMany.defaults['destinationAttribute'] =3D RToMany.attNameFromRel # fk+d= estEnt+sourceAtt+count # Note that Relation.attNameFromRel is a callable, that calculates the att # name from the indicated pieces (where count to distinguish between multip= le # relations between same source and target Entity.defaults['properties'] =3D [ APrimaryKey('id', isClassProperty=3D0, isRequired=3D1, doc=3D'Primary key= !') ] ## _connDict =3D {} model =3D Model('StoreEmployees',adaptorName=3D'Postgresql',connDict=3D_con= nDict) model.doc =3D ' ... ' model.version=3D'0.1' model.entities =3D [ # Entity('Store', properties=3D[ AString('corporateName', isRequired=3D1), RToMany('employees', 'Employee', inverse=3D'toStore')= =20 ], doc =3D 'The Store object ...' ), # Employee and its subclasses SalesClerk and Executive Entity('Employee', properties=3D[ AString('lastName', isRequired=3D1, usedForLocking= =3D1),=20 AString('firstName', isRequired=3D1, width=3D50, usedForLocking=3D1), RToMany('toAddresses', 'Address', delete=3D2),#'casca= de'),=20 RToOne('toStore', 'Store')=20 ] ), Entity('SalesClerk', parent=3D'Employee', properties=3D[ AString('storeArea')=20 ] ), Entity('Executive', parent=3D'Employee', properties=3D[ AString('officeLocation', width=3D5), RToMany('marks', 'Mark', delete=3D2, #'cascade' inverse=3D'executive') ] ), =20=20 # Entity('Address', properties=3D[ AString('street', width=3D80),=20 AString('zipCode'),=20 AString('town', width=3D80), RToOne('toEmployee', 'Employee', delete=3D1,#'deny' inverse=3D'toAddresses')=20 ] ), =20=20 # Entity('Mark', properties=3D[ AInteger('month', isRequired=3D1),=20 AInteger('mark', isRequired=3D1), AForeignKey('toExecutive', isClassProperty=3D0), RToOne('executive', 'Executive', src=3D'toExecutive', dst=3D'id', inverse=3D'marks'), ] ) =20=20 ] if __name__ =3D=3D '__main__': model.build() #print model.validate() #print model.toXML() # plus whatever ... =20=20 ## ------------------------------------------------------------------------ |