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