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=
> -
|