Re: [Modeling-users] Pythonic, and non-XML, Model description
Status: Abandoned
Brought to you by:
sbigaret
|
From: Mario R. <ma...@ru...> - 2003-02-28 11:32:17
|
Hello,
thanks for the detailed feedback, and I like most of it very much.
I comment on each point (needing comment) below...
...
>> The behaviour in general would stay the same as the XML way of
>> doing things, namely form this model you would generate the classes
>> and the db schemas.
>
> These features are available for any Modeling.Model object, so the
> "only"
> thing to do is build it from the py-model, just like it goes for the
> xml.
>
> (same for generating an xml-model, thus mapping a py-model to an
> xml-model
> will be straightforward --cf. ModelSet.getXMLDOMForModelNamed() and
> getXMLStreamForModelNamed())
Yes, should be the same idea, except that there'd be no need to have
methods
with "forModelNamed" in their name, as this would be implied by the
object on
which the method is called:
modelset.getXML()
modelset.getXMLDOM()
model.getXML()
entity.getXML()
...
The ModelSet concept is not yet included in the PyModel description
(mostly
becuase it is not fully clear to me how it is used). How would you
extend this
description to include it?
>> Some differences are choice of default values, and that key and
>> locking
>> attributes are defined on attributes and relations rather than
>> entities
>> ;)
>
> Ok, I guess we won't argue any further on this by now ;)
But, see how it makes things easier! You declare a key on an att, you
use
the att, defining also teh key on the using entity -- makes things
consistent,
and less wordy. If you want that an id in one entity is a key but not
in another,
then you are forced to define 2 different id 'types', which helps make
the
difference obvious to anyone looking in.
>> As an exercise, I have taken the sample model in the distribution:
>> testPackages/StoreEmployees/model_StoreEmployees.xml and re-expressed
>> it
>> in this pythonic way.
>
> Thanks, this made things even clearer.
>>
>> Apart from readability and losing of some verbosity, other gains are
>> "executability" and dynamicity -- it could be very easy to modify a
>> model
>> at runtime, if you would ever want to do that.
>
> Oh oh, I can't believe you said that! Models' mutability at runtime is
> an
> other subject we'd better discuss apart from this thread. It *is*
> possible,
> under some conditions I certainly need to gather before answering. If
> you
> need this sort of feature in short-term, you'd better picked me on
> wildly!!
In general this is not what anyone woudl want to do, and I was trying
to think
of times when this would be useful. Can't really, but i *suspect* it
might come
in handy. But, in fact, imagine some application that is stocking lots
of
incoming unknown data into a db... let's say the data is XML, but each
XML
data-schema is unknown a priori. You can create a py model to
correspond,
at runtime (but saved for subsequent accessing), create the tables, and
stock the unknown XML in the db directly as real db tables....
>> Also, given that a model now becomes a module in and of itself, it
>> gains
>> from what modules have to offer, such as self-tests.
>
> Ok.
>
>> And, speed of course, as i see no reason why this model object would
>> also
>> not serve as the model object in memory at runtime (but that's for
>> Sebastien to confirm) -- thus loading the model is equivalent to "from
>> MyModel import model".
>
> Well, it will need a few more lines of code to actually load the model
> into
> the so-called defaultModelSet (cf. Modeling.ModelSet and the generated
> __init__.py), but that's it.
Yes, but i was leaving all that to you ;)
But, details of how such a py model would integrate with the rest of
the framework are all to be decided, and please point out any problems
that only you can foresee!
>> Can you please take a look at the 2 linked files below (as temporary
>> URLs, as to attach they are too big);
>> - PyModel.py -- defines the classes (signatures for) for, and
>> documents the rules for, a PyModel instance
>> - sample_PyModel.py -- re-expresses the StoreEmployees model
>
> Ok, now let's take a look at the big stuff ;)
>
> Your python code is sound and clear. My first comments are:
A general comment to the comments below -- in general i do not like
'special format strings' as values to things. This adds possible errors,
requires special documentation, makes checking more difficult, and
in general is less pythonic... I think keeping the info units separate
would be easier to work with, and clearer. The overhead of having
to specify the same parameters many times over can be reduced
by standard defaults and the possibility to set defaults only for
this model (see new sample...)
> - maybe we'll gain more readability if relationships' multiplicity
> lower-
> and upper bounds where encoded in strings, such as: '0-1', '1-1',
> '0-*'
> or '2-16'.
Yes, but not as strings. What is wrong with a python list?
multiplicity = [0,1]
multiplicity = [1,-1]
> - Same for external type's width, precision and scale, such as in:
> 'NUMERIC(12,2)' or 'VARCHAR(200)'
Same thing here. This would also make it difficult to provide defaults
separately for dbtype, and for width or precision.
So, I would vote to keep them separate (and tyo change the name of
externalType to dbType). I think it will be easier to work with,
and clearer for everyone.
> - Again, same for an entity's parent which could be specified along
> with
> the entity's name: 'Executive(Employee)'
Same. I vote to keep separate, and to rename parentEntity to isAlso,
e.g.
Entity('Executive', isAlso='Employee', ...
> - What about the possibility to add a '*' to an attribute's name
> when it's
> required? (may be same for a relationship, equivalent to lower
> bound==1)
Same for adding '*' to att name, but definately yes to have constraints
forced
down as a result from relations, e.g. if a one-to-one relation is
required, then
the related attributes must also be required -- but this is automatic
and taken
into account by the validation.
> - Allow litterals instead of the equivalent integers in the xml:
> 'CASCADE'
> for the delete rule is more explicit than int(2)!
Definately. Also, in lowercase! (Hate being screamed at, which is what
uppercase seems to be always doing \-)
> - I think I would have made 'name' instead 'columnName' the default
> for
> displayLabel :)
Yes, it is probably better.
> Now that I get used to the idea of a python-model, I'm also thinking
> of some
> extents to your proposal:
>
> - We could have subclasses for your Attribute: PrimaryKey, ForeignKey
> (defaults for both would be: int, not class property, etc.), String
> (with a default external size/width), Integer, Numeric, ... You
> get the
> idea.
Great! There could be some standard sub-classes, but a user is
ofcourse allowed to make his own. What about the naming scheme propoes
by the example below, that Att subs start with the letter 'A' and rel
subs start
with the letter 'R' ? (to avoid unnecessarily long names)
> - Relation can also be sub-classed to 'ToOne' and 'ToMany' (default
> would
> be '0-*' for the latter)
Yes. Also, all defaults may be set for a specific model as necessary...
> Last, I'm thinking of some automatic processing which is already coded
> in
> the zmodeler and that could be done at model-time to reduce verbosity
> in a
> significant manner:
>
> - have a primary key 'id' automatically declared if not set,
In general "magical" behaviour is more trouble than gain... how about if
we have the possibility to define a default attribute on an Entity
description class?
> - have foreign keys automatically set for relationships, using the
> same
> defaults the zmodeler already uses (e.g. FKEmployeeId for a to-many
> relationship pointing to the entity Employee). It would need some
> additional checks but it's definitely possible.
Again, I like simplifying the management of all this, but i do not like
being
forced to accept decisions imposed by the framework, such as the names
of
my columns (what if I want to provide a model for an existing db?)
So, providing a default scheme, that the user may redefine (or
not use at all), seems more reasonable to me. See the defaults for
sourceAttribute
and destinationAttribute in RToOne and RToMany... Also, see the explicit
declaration of the foreign keys (with the possibility of automatic the
name
for it).
> This could be as handy as the feature you submitted in your proposal:
> automatic definition parent properties which are not overriden in
> sub-entities.
Yes. Auto generation of foreign keys from a relationship definition does
make sense in fact... but the possibility to declare them explicitly
(with
the desired values for their attributes) should always be there. Since
such explicit declaration can be very simple, I prefer to leave it as
must be explicit.
> I wrote all these items with something in mind, actually. You know, I'm
> basically lazing, most of the time I do not take about DB-Schemas
> details,
> and I will be delighted if it was possible to write the same
> StoreEmployee
> model really simply. This is a good illustration of the automatic
> processing
> I described above because this model (like every test models) were
> designed
> using the zmodeler and its functionalities.
>
> In fact, I'm thinking that something simple like that could be
> written:
> (this would imply some changes to your proposed API, such as making
> 'destinationEntity' the second argument for the Relationship's
> initializer).
Ah, very good -- destinationEntity must in fact be specified everytime,
so
this is better this way.
I have taken your simplified model, and evolved it to correspond to my
comments above. Correspondingly, i have also updated the PyModel
module to indicate how it would change to support these changes.
I have put the two files (PyModel_3_mr.py and sample_ PyModel_3_mr.py)
at:
http://ruggier.dyndns.org:8080/PyModel/
But for convenience, I am also pasting below the sample model...
Oh, and some other changes I have not mentioned here are listed in
the top of PyModel_3_mr.py, but mostly name changes, and having
entities have only one list, that mixes attributes and relations...
(your opinion?).
> Of course this would need some additional cpu-time to load a model,
> but I
> bet it would be far quicker than parsing the xml!
>
> This of course still needs to be discussed and refined. I did not
> have any
> time to try & implement my proposal, but I guess that at some point of
> the
> discussion we'll need to see the words take shape --at this point this
> could
> naturally be made in a dev-branch if several of us are on this.
OK.
> I really have the feeling that we will succeed in designing a very
> nice
> python model. Let's go for it now that Mario brought some light on
> the
> path!
That would not be XPath, would it ;-?
Cheers, mario
ps: sample_ PyModel_3_mr.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 type
Model
'''
from PyModel import *
##
# Set preferred defaults for this model (when different from
# standard defaults, or if we want to make things explicit)
AInteger.defaults['precision'] = 10
AString.defaults['width'] = 20
RToOne.defaults['delete'] = 'cascade'
RToOne.defaults['multiplicity'] = [0,1]
RToOne.defaults['sourceAttribute'] = RToOne.attNameFromRel #
'fk'+destEnt+destAtt+count
RToOne.defaults['destinationAttribute'] = 'id'
RToMany.defaults['delete'] = 'deny'
RToMany.defaults['multiplicity'] = [0,*]
RToMany.defaults['sourceAttribute'] = 'id'
RToMany.defaults['destinationAttribute'] = RToMany.attNameFromRel #
fk+destEnt+sourceAtt+count
# Note that Relation.attNameFromRel is a callable, that calculates the
att name
# from the indicated pieces (where count to distinguish between
multiple relations
# between same source and target
Entity.defaults['attributes'] = [
AInteger('id', key=1, isClassProperty=0, isRequired=1)
]
##
_connDict = {}
model = Model('StoreEmployees',connDict=_connDict)
model.entities = [
#
Entity('Store',
attributes=[
AString('corporateName', isRequired=1),
RToMany('employees', 'Employee')
]
)
# Employee and its subclasses SalesClerk and Executive
Entity('Employee',
attributes=[
AString('lastName', isRequired=1, usedForLocking=1),
AString('firstName', isRequired=1, width=50,
usedForLocking=1),
AForeignKey(Attribute.fk('Store'), isClassProperty=0),
RToMany('toAddresses', 'Address', delete='cascade'),
RToOne('toStore', 'Store')
]
)
Entity('SalesClerk', isAlso='Employee',
attributes=[
AString('storeArea')
]
),
Entity('Executive', isAlso='Employee',
attributes=[
AString('officeLocation', width=5),
RToMany('marks', 'Mark', delete='cascade')
]
),
#
Entity('Address',
attributes=[
AString('street', width=80),
AString('zipCode'),
AString('town', width=80),
AForeignKey(Attribute.fk('Employee'), isClassProperty=1),
RToMany('toEmployee', 'Employee', delete='deny')
]
),
#
Entity('Mark',
attributes=[
AInteger('month', isRequired=1),
AInteger('mark', isRequired=1),
AForeignKey(Attribute.fk('Executive'), isClassProperty=1),
RToOne('executive', 'Executive' ]
]
)
]
if __name__ == '__main__':
print model.validate()
print model.toXML()
# plus whatever ...
##
|