Re: [Modeling-users] Pythonic, and non-XML, Model description
Status: Abandoned
Brought to you by:
sbigaret
From: Mario R. <ma...@ru...> - 2003-05-16 12:18:33
|
On jeudi, mai 15, 2003, at 20:50 Europe/Amsterdam, Sebastien Bigaret =20 wrote: > Hi there, > > More comments about PyModels --we need it in release 0.9 for the 1 > page tutorial to be really efficient. Great! > 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 =20 > 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) Like the idea a lot. It is better than using relationships because it encapsulates the info about both ends in one place. Can it replace Relationships (in client models) entirely? > - defaults: > > 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 = =20 > x:x) > 'name' is the name of the object's (model's) attribute to =20= > pass > as an attribute (it should be an object's attribute) > > When multiple parameters are needed: > MP(function,'param1','param2') OK for me. My only question is that (frequently used) functions to be =20= passed to MP should be included in MP, or close by, as helper funtions, > ii. sample_PyModel_4 said: > > Entity('Employee', > properties=3D[ # [...] > AForeignKey(Attribute.fk('Store'), > isClassProperty=3D0), > ] ) > > --> Attribute.fk('<destEntity>') was not a good idea, it's even =20= > not > feasible. So: either you declare (and name) the foreign key, > or you rely on the initialization of a relationship to = create > it. OK. > - 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', > AForeignKey('fkStore'), > =20 > properties=3D[RToOne('toStore','Store',src=3D'fkStore',dst=3D'id')] > ) > > 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 =20 > specified > in either relationships, or in both. Example: > > Entity('Employee', properties=3D[ RToOne('toStore', 'Store', > inverse=3D'toEmployees') = ] =20 > ) > > Both a. and b. are implemented. > In case b. the foreign key is created in the source (resp. > destination) entity for a RToOne (resp. RToMany) relationship I suggest there should be (at least as recommendation) only one way. I would prefer (b), as it is more expressive. But, as mentioned above, this may all be pushed further down, and client model code should only deal with Associations? > - 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) This is nice. Compact and expressive. Have you already mapped (in full, taking out relationships) the StoreEmp model to using Associations? > > 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' }, # =20 > Emp.toStore > { 'doc': 'toMany: Store to Employee' }, # =20 > Store.toEmp. > ) > ] > > I'm not sure this looks pretty, so if you have ideas they are > welcome ;) One point I would like to stress is that there should be no bizarre variations between how a parameter is specified in one object or another. Thus Associations should try to follow as much as possible the same semantics as the others -- which is that required parameters are not named, and al other optional parameters are named and have defaults. Introducing a dictionary param will break that consistency... Anyway, how about: Association('E1','E2', rel =3D ['toE2', 'toE1' ] multiplicity=3D [ [0,1], [0,None] ], doc =3D ['to one', 'to many' ], ... ) Defaults will be handled identically to other classes. > Now for the details: > > * multiplicity: unconstrained upper bound can be -1, '*' or None > > * I choose 'required' instead of 'allowsNone' for attributes/rels. OK for me. > Enclosed is a (working) version of sample_PyModel_5.py Nice. Version with Associations and no Relationships? Also, it may be a good idea to include the new way how to handle conndict info, even in these examples. Plus, see minor comment below. mario > 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. > > = -----------------------------------------------------------------------=20= > - > ''' > sample_PyModel_5.py > A sample Pythonic OO-RDB Model > (re-expressing testPackages/StoreEmployees/model_StoreEmployees.xml) = -- > A PyModel must define a global variable called 'model', that is of =20 > type Model > ''' > > from PyModel import * > > ## > # Set preferred defaults for this model (when different from > # 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] > #RToOne.defaults['sourceAttribute'] =3D RToOne.attNameFromRel # =20 > 'fk'+destEnt+destAtt+count > #RToOne.defaults['destinationAttribute'] =3D 'id' > > RToMany.defaults['delete'] =3D 1 # 'deny' # this is temporary, final =20= > version > # will say =20 > 'deny'/'cascade'/'nullify' > # as expected > RToMany.defaults['multiplicity'] =3D [0,None] > #RToMany.defaults['sourceAttribute'] =3D 'id' > #RToMany.defaults['destinationAttribute'] =3D RToMany.attNameFromRel # = =20 > fk+destEnt+sourceAtt+count > > # Note that Relation.attNameFromRel is a callable, that calculates the = =20 > att > # name from the indicated pieces (where count to distinguish between =20= > multiple > # relations between same source and target > > Entity.defaults['properties'] =3D [ > APrimaryKey('id', isClassProperty=3D0, isRequired=3D1, doc=3D'Primary = =20 > key!') > ] > > ## > > _connDict =3D {} > model =3D =20 > Model('StoreEmployees',adaptorName=3D'Postgresql',connDict=3D_connDict) > model.doc =3D ' ... ' > model.version=3D'0.1' > model.entities =3D [ > > # > Entity('Store', > properties=3D[ AString('corporateName', isRequired=3D1), > RToMany('employees', 'Employee', =20 > inverse=3D'toStore') > ], > doc =3D 'The Store object ...' > ), > > # Employee and its subclasses SalesClerk and Executive > Entity('Employee', > properties=3D[ AString('lastName', isRequired=3D1, =20 > usedForLocking=3D1), > AString('firstName', isRequired=3D1, width=3D50, > usedForLocking=3D1), > RToMany('toAddresses', 'Address', =20 > delete=3D2),#'cascade'), > RToOne('toStore', 'Store') > ] > ), > > Entity('SalesClerk', parent=3D'Employee', > properties=3D[ AString('storeArea') > ] > ), > > Entity('Executive', parent=3D'Employee', > properties=3D[ AString('officeLocation', width=3D5), > RToMany('marks', 'Mark', delete=3D2, #'cascade' > inverse=3D'executive') > ] > ), > > # > Entity('Address', > properties=3D[ AString('street', width=3D80), > AString('zipCode'), > AString('town', width=3D80), > RToOne('toEmployee', 'Employee', = delete=3D1,#'deny' > inverse=3D'toAddresses') > ] > ), > > # > Entity('Mark', > properties=3D[ AInteger('month', isRequired=3D1), > AInteger('mark', isRequired=3D1), > AForeignKey('toExecutive', isClassProperty=3D0), *** given your changes, is this needed (AForeignKey toExecutive) ? > RToOne('executive', 'Executive', > src=3D'toExecutive', dst=3D'id', > inverse=3D'marks'), > ] > ) > > ] > > if __name__ =3D=3D '__main__': > model.build() > #print model.validate() > #print model.toXML() > # plus whatever ... > > ## > = -----------------------------------------------------------------------=20= > - |