Thread: [Modeling-users] API cleanup proposal
Status: Abandoned
Brought to you by:
sbigaret
|
From: Sebastien B. <sbi...@us...> - 2003-06-02 18:12:10
|
Hi all,
Here is a proposal to change some of the most commonly used methods;
the general idea is to improve developers' experience with the framework
by providing better, cleaner, shorter names for existing functionalities.
[Note on "most commonly used methods": I do not plan to change every
method's name when it's too long: they are mostly for the framework's
use, or at best for advanced usage]
EditingContext (remember: an EC can be thought as a transaction)
--------------
- insert() instead of insertObject()
- delete() instead of deleteObject()
- fetching: remove the need to import FetchSpecification and
Qualifier, and propose an alternate for
objectsWithFetchSpecification()
def fetch(self, entityName,
qualifier=3DNone, # either a Qualifier instance or a string
orderBy=3DNone, # either a SortOrdering or a string
isDeep=3D0, # should subentities be fetched as well?
refresh=3D0, # unsupported yet
lock=3D0, # unsupported yet
limit=3DNone, # \
page=3DNone, # > slice parameters, not in core yet
offset=3DNone, # / (page/offset: mutually exclusive)
)
=20=20=20=20
Typical usage:
ec.fetch('Writer', 'lastName ilike "hu?o"')
ec.fetch('Writer', 'age>=3D80"')
ec.fetch('Writer', 'author.pygmalion.lastName caseInsensitiveLike "r*=
"')
ec.fetch('Writer', orderBy=3D'firstName asc, lastName desc', isDeep=
=3D1)
ec.fetch('Books', orderBy=3D'title', limit=3D10, page=3D1)
- replace objectsCountWithFetchSpecification() with fetchCount() ?
[same API as fetch()]
- replace (set)propagatesInsertionForRelatedObjects() with
autoInsertion() and setAutoInsertion()
CustomObject
------------
I don't see any methods to change here. Maybe we could add globalID()?
However, the name itself 'CustomObject' could be changed if you have
some ideas (I admit it is not very representative of what it does,
i.e. defining generic methods for an object to be bound w/ the
framework)
Validation
----------
Do we need to change anything there? validateValueForKey() is the
longest one, however I think this is not widely used, is it?
KeyValueCoding
--------------
As Mario repeatedly pointed it out some time ago, this module needs to
be cleaned.
Core methods:
| [public access] | [private access]
------+---------------------------+----------------------------------
[get] |valueForKey() | storedValueForKey()
|valueForKeyPath() |
------+---------------------------+----------------------------------
[set] |takeValueForKey() | takeStoredValueForKey()
|takeValueForKeyPath() |
------+---------------------------+----------------------------------
[dict]|takeValuesFromDictionary() | takeStoredValuesFromDictionary()
=20=20
Quick reminder: KVC is intensively used by the framework to get & set
an object's values without knowing where it is
actually stored (for example, attribute 'name' can be
stored in name, _name, and/or can be accessed/set via
getters/setters name/getName()/setName())
The framework *always* uses the so-called "private" access
methods. The reason is that e.g. a public setter can have some
side-effect that should be triggered from a user's point of view (at
creation or modification time), but shouldn't be triggered when
object's data are fetched from the db (this is not very common,
though, hence I think we need a simplified and more efficient KVC for
the common cases --off-topic here).
This is my proposal:
* getProp(name, path):
getProp(key) replaces valueForKey(key)
getProp(path=3DkeyPath) replaces valueForKeyPath(keyPath)
* setProp(name, path):
setProp(key) replaces takeValueForKey(key)
setProp(path=3DkeyPath) replaces takeValueForKeyPath(keyPath)
* same for getStoredProp() and setStoredProp() which replace
(take)storedValueForKey
=20=20
Notes: - parameters name & path are obviously mutually exclusive,
- get/setStoredProp shouldn't be needed from a developer's point
of view.
* updateProps() replaces takeValuesFromDictionary()
updateStoredProps() replaces takeValuesFromStoredDictionary()
Along with that, we also need an other module/class name for
KeyValueCoding.
Mario proposed PropValueAccess. Any ideas?
GenericAccess? PropHandling?
RelationshipManipulation
------------------------
Any thoughts about how addObjectToBothSidesOfRelationshipWithKey() and
removeObjectFromBothSidesOfRelationshipWithKey() can be renamed?=20
=3D> An other solution could be: gather KVC and RelationshipManipulation
in a single module, since they both address the same thing: generic
manipulation of an object, given its definition in the model. What do
you think?
BTW this is not a definitive list, just the things I know that need to
be changed. If you feel like changing other names please tell.
Your feedback is greatly appreciated!-)
-- S=E9bastien.
|
|
From: Yannick G. <ygi...@yg...> - 2003-06-03 02:45:56
|
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 02 June 2003 14:10, Sebastien Bigaret wrote: > [Note on "most commonly used methods": I do not plan to change every > method's name when it's too long: they are mostly for the framework's > use, or at best for advanced usage] Please make aliases for backward compatibility. : ) The proposal sounds really fair to me ! - -- Yannick Gingras Coder for OBB : Obscenely Base Bottlegrass http://OpenBeatBox.org -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.7 (GNU/Linux) iD8DBQE+3Avarhy5Fqn/MRARAqX8AJsHZcGE2QW+Uf+N8su6mCkkCJeCSACgjhEe Ad0Tl6Gqiw9xV3J4lUdjDyA= =fNcy -----END PGP SIGNATURE----- |
|
From: Sebastien B. <sbi...@us...> - 2003-06-04 19:27:16
|
Yannick Gingras <ygi...@yg...> wrote: > On Monday 02 June 2003 14:10, Sebastien Bigaret wrote: > > [Note on "most commonly used methods": I do not plan to change every > > method's name when it's too long: they are mostly for the framework's > > use, or at best for advanced usage] > > Please make aliases for backward compatibility. >=20 > : ) >=20 > The proposal sounds really fair to me ! A quick answer --sorry, I'm quite busy these days. When I wrote: "method_1() replaces by method_2()", obviously I meant *is an alias for*. I just want to make it clear that I pay a careful attention to backward compatibility and that it won't be broken in any ways after the API "changes"/upgrades. I'll comment further on the proposal and answers later, probably on sunday. -- S=E9bastien. |
|
From: Mario R. <ma...@ru...> - 2003-06-03 08:06:55
|
> Hi all,
>
> Here is a proposal to change some of the most commonly used methods;
> the general idea is to improve developers' experience with the=20
> framework
> by providing better, cleaner, shorter names for existing=20
> functionalities.
I would also add "by indicating clearly the small subset of methods=20
intended
for use by client code, and the stability level".
> [Note on "most commonly used methods": I do not plan to change every
> method's name when it's too long: they are mostly for the =
framework's
> use, or at best for advanced usage]
> EditingContext (remember: an EC can be thought as a transaction)
> --------------
>
> - insert() instead of insertObject()
>
> - delete() instead of deleteObject()
OK. What about commit() for saveChanges()?
> - fetching: remove the need to import FetchSpecification and
> Qualifier, and propose an alternate for
> objectsWithFetchSpecification()
>
> def fetch(self, entityName,
> qualifier=3DNone, # either a Qualifier instance or a=20=
> string
> orderBy=3DNone, # either a SortOrdering or a string
> isDeep=3D0, # should subentities be fetched as=20=
> well?
> refresh=3D0, # unsupported yet
> lock=3D0, # unsupported yet
> limit=3DNone, # \
> page=3DNone, # > slice parameters, not in core =
yet
> offset=3DNone, # / (page/offset: mutually =
exclusive)
> )
>
> Typical usage:
>
> ec.fetch('Writer', 'lastName ilike "hu?o"')
> ec.fetch('Writer', 'age>=3D80"')
> ec.fetch('Writer', 'author.pygmalion.lastName=20
> caseInsensitiveLike "r*"')
> ec.fetch('Writer', orderBy=3D'firstName asc, lastName desc',=20
> isDeep=3D1)
> ec.fetch('Books', orderBy=3D'title', limit=3D10, page=3D1)
Yes, this is much easier than using FetchSpecification and=20
QualifierFormats...
I feel that some things (even if no one has requested them yet ;) are=20
missing...
for example, should one ever need to take advantage of sql "group by"=20
and "having".
Access to these sql clauses may be added later, without breaking this=20
API,
which is OK. Also, such manips may be done in the middle code, but
that would be very inefficient.
Another issue is that even with all the keyword options offered, one=20
still
has to "dip into" small pieces of raw sql very quickly, as shown by your
examples. This may be an acceptable compromise, but then client code
should be allowed to pass any SQL it wants...
Another detail is that the object loads all (direct) properties, every=20=
time --
there may be no workaround for this, given the way the framework is=20
built.
The real problem with this is that to request the result of an sql=20
function,
of which count() is an example, additional api functionality is needed.
But what happens if I want other functions, such as SUM or MAX or=20
AVERAGE
over a few columns? Each of these functions may take complex parameters.
Again, the functionality may be replicated in the middle code, but this=20=
would
not only be a waste of development time, but also be very inefficient.
I propose either a generalization of "select" (which may be too=20
complicated
in its implications), or an addition of a func keyword option, e.g.
func=3D'count'
func=3D('funcName', param1, param2, ...)
func=3D( ('funcName', param1, param2, ...), ('funcname2') )
func=3D('sum','age')
The question is then how is this information returned?
I suggest as a tuple...
Which reminds me that it would be nice to have a convenience
"resultset" option, that when true returns results as a db resulset.
This implies that one should not mix, in the same non-resultset fetch,
requests for real objects and for additional "virtual" properties,
such as counts, sums, etc. (Or stated differently, the presence
of func will cause data to be returned as a tuple.)
> - replace objectsCountWithFetchSpecification() with fetchCount() ?
> [same API as fetch()]
I propose use fetch() as descibed above:
ec.fetch('Writer', qualifier=3D'age>=3D80"', func=3D'count')
=3D> (3)
but also:
ec.fetch('Writer', qualifier=3D'age>=3D80"',=20
func=3D(('count'),('max','age'),('average','age')) )
=3D> (3,91,85)
[ or else we can stick to raw sql:
ec.fetch('Writer', qualifier=3D'age>=3D80"', func=3D'count(*), max(age),=20=
average(age)' )
which may be simpler for everyone... in this case might as well change=20=
'func' to 'select',
which will always assume a 'resulset' response ]
> - replace (set)propagatesInsertionForRelatedObjects() with
> autoInsertion() and setAutoInsertion()
I prefer setAutoInsertion()
>
> CustomObject
> ------------
>
> I don't see any methods to change here. Maybe we could add=20
> globalID()?
> However, the name itself 'CustomObject' could be changed if you have
> some ideas (I admit it is not very representative of what it does,
> i.e. defining generic methods for an object to be bound w/ the
> framework)
CustomObject will inherit the changes to the KeyValueCoding API,
so it should be OK.
CustomObject as name is not so bad. Other names that come to
mind are MappedObject, ManagedObject, MorfedObject ;)
PersistedObjected
> Validation
> ----------
>
> Do we need to change anything there? validateValueForKey() is the
> longest one, however I think this is not widely used, is it?
I suggest validateProperty() instead of validateValueForKey().
>
> KeyValueCoding
> --------------
>
> As Mario repeatedly pointed it out some time ago, this module needs=20=
> to
> be cleaned.
>
> Core methods:
>
>
> | [public access] | [private access]
> =20
> ------+---------------------------+----------------------------------
> [get] |valueForKey() | storedValueForKey()
> |valueForKeyPath() |
> =20
> ------+---------------------------+----------------------------------
> [set] |takeValueForKey() | takeStoredValueForKey()
> |takeValueForKeyPath() |
> =20
> ------+---------------------------+----------------------------------
> [dict]|takeValuesFromDictionary() |=20
> takeStoredValuesFromDictionary()
>
> Quick reminder: KVC is intensively used by the framework to get & =
set
> an object's values without knowing where it is
> actually stored (for example, attribute 'name' can =
be
> stored in name, _name, and/or can be accessed/set =
via
> getters/setters name/getName()/setName())
>
> The framework *always* uses the so-called "private" access
> methods. The reason is that e.g. a public setter can have some
> side-effect that should be triggered from a user's point of view (at
> creation or modification time), but shouldn't be triggered when
> object's data are fetched from the db (this is not very common,
> though, hence I think we need a simplified and more efficient KVC =
for
> the common cases --off-topic here).
>
> This is my proposal:
>
> * getProp(name, path):
>
> getProp(key) replaces valueForKey(key)
> getProp(path=3DkeyPath) replaces valueForKeyPath(keyPath)
I like the use of path instead of another method. However does
this imply an extra check per call? Also, when specified path must
include name... is this better than path must never include name?
> * setProp(name, path):
>
> setProp(key) replaces takeValueForKey(key)
> setProp(path=3DkeyPath) replaces takeValueForKeyPath(keyPath)
>
> * same for getStoredProp() and setStoredProp() which replace
> (take)storedValueForKey
>
> Notes: - parameters name & path are obviously mutually exclusive,
> - get/setStoredProp shouldn't be needed from a developer's=20
> point
> of view.
>
> * updateProps() replaces takeValuesFromDictionary()
> updateStoredProps() replaces takeValuesFromStoredDictionary()
OK for this, but I would in these case prefer to use the full word=20
(respecting
the naming convention so far...), i.e.:
co.getProperty()
co.setProperty()
co.getStoredProperty()
co.setStoredProperty()
co.updateProperties()
co.updateStoredProperties()
> Along with that, we also need an other module/class name for
> KeyValueCoding.
>
> Mario proposed PropValueAccess. Any ideas?
> GenericAccess?=20
> PropHandling?
>
I would be in favour of combining with RelationshipManipulation...
> RelationshipManipulation
> ------------------------
>
> Any thoughts about how addObjectToBothSidesOfRelationshipWithKey()=20=
> and
> removeObjectFromBothSidesOfRelationshipWithKey() can be renamed?
How about addViaRelation() and removeViaRelation()
> =3D> An other solution could be: gather KVC and =
RelationshipManipulation
> in a single module, since they both address the same thing: generic
> manipulation of an object, given its definition in the model. What=20=
> do
> you think?
Yes. How about MappedObject PR?
For Properties and Relations of course ;)
Module name could be "mopr" or simply "opr".
> BTW this is not a definitive list, just the things I know that need to
> be changed. If you feel like changing other names please tell.
>
>
> Your feedback is greatly appreciated!-)
>
>
> -- S=E9bastien.
mario
|
|
From: Sebastien B. <sbi...@us...> - 2003-06-12 14:35:32
|
Hi all,
Some more comments about the API change proposal; I concentrate here on
EditingContext, and will post another message for CustomObject and
related APIs.
Note: I also received two private answers.
* Again: methods will be aliased, not replaced,
* EC.insert() and EC.delete() are okay to everyone.
* autoInsertion()/setAutoInsertion(): the former is a getter, the
latter, a setter
Mario> OK. What about commit() for saveChanges()?
* about commit(): that was initially in my proposal. I removed
it. Remember I'm working on integrating the ZODB transaction and
observation mechanisms into the modeling? Well, then, commit() clashes
with py2.1 zodb's transaction protocol (and has a very different
semantic than saveChanges(): it is equivalent to objectWillChange()).
Hence I'd prefer to let it apart from the proposal for the moment
being.
* EC.fetch():
The initial proposal is accepted by everyone so we'll keep it as a basis.
Mario> Another issue is that even with all the keyword options offered, one
Mario> still has to "dip into" small pieces of raw sql very quickly, as sho=
wn
Mario> by your examples.
Here I strongly disagree with you. It /seems/ that you dip into small
pieces of raw sql, but it's not the case at all. Qualifiers and
FetchSpecification properties offers a generic command language;
consider this:
ec.fetch('Book', 'author.pygmalion.lastName ilike "r*"')
-> this is far from the generated sql:
SELECT t0.id, t0.title, t0.FK_WRITER_ID, t0.PRICE=20
FROM BOOK t0=20
INNER JOIN ( WRITER t1=20
INNER JOIN WRITER t2=20
ON t1.FK_WRITER_ID=3Dt2.ID )
ON t0.FK_WRITER_ID=3Dt1.ID
WHERE UPPER(t2.LAST_NAME) LIKE UPPER('r%');
In the framework we generally make the strong assumption that raw sql
is taken out of the python code. Even if some of the keywords (such as
'like'/'ilike' for qualifiers, or 'asc'/'desc' for ordering) are
actually the same than the corresponding sql keywords, they are indeed
decorrelated. See again the example above: the 'ilike' keyword is not
used, rather we compare UPPER()s (this might change in the future, now
that 'ilike' is a sql keyword accepted by a lot of db-servers ;)
Mario> This may be an acceptable compromise, but then client code should be
Mario> allowed to pass any SQL it wants...
That's an other problem, to which I completely agree: we should have a
mean to execute complete raw sql statements, and get the (raw) result
back. Moreover, we should be able to transform the returned raw rows
into real objects if necessary (and if possible).
> Another detail is that the object loads all (direct) properties, every
> time -- there may be no workaround for this, given the way the
> framework is built.
(also related to your 'resultset' proposal)
Last, we should also be able to tell the framework to return the raw
rows instead of fully initialized objects (which can later be reverted
to real objects): there's a real need for that; sometimes you want to
present a (very) long list of objects in a summary page/widget, but
you do not need the full objects, not even every attributes, but a
subset to present the user. Then the users selects one or more of this
rows and that's where you'll transform the raw rows to real objects.
Note that the framework architecture will have no problem to support
this. Some of the needed APIs are already present but not implemented
(such as DatabaseContext.faultForRawRow()).
I thought this was on the todo list but it's not --I'll add that,
since I've been thinking about these points for quite a long time.
Impact on the API:
when this is implemented I suggest we add the following parameters to
fetch():
rawRows -- (default: false) return raw rows instead of objects
sql -- execute the sql statement (and ignore all other parameters
except entityName and rawRows --both optional in that case)
BTW I also suggest that the unsupported features in the fetch API are
removed until they are implemented (such as limit/page/offset/etc).
> I feel that some things (even if no one has requested them yet ;) are
> missing...
> for example, should one ever need to take advantage of sql "group by" and
> "having".
> Access to these sql clauses may be added later, without breaking this API,
> which is OK. Also, such manips may be done in the middle code, but
> that would be very inefficient.
[...]
>=20
> The real problem with this is that to request the result of an sql functi=
on,
> of which count() is an example, additional api functionality is needed.
> But what happens if I want other functions, such as SUM or MAX or AVERAGE
> over a few columns? Each of these functions may take complex parameters.
> Again, the functionality may be replicated in the middle code, but this w=
ould
> not only be a waste of development time, but also be very inefficient.
>=20
> I propose either a generalization of "select" (which may be too complicat=
ed
> in its implications), or an addition of a func keyword option, e.g.
>=20
> func=3D'count'
> func=3D('funcName', param1, param2, ...)
> func=3D( ('funcName', param1, param2, ...), ('funcname2') )
>=20
> func=3D('sum','age')
>=20
> The question is then how is this information returned?
> I suggest as a tuple...
[snipped]
That's a very interesting idea, but this will take too much efforts
for it to be shortly developped. Let me explain that: if we do this
just as it sounds, then we will have *pieces* of raw sql in the middle
of generic command patterns --I don't like that. Do not misunderstand
me: it should be possible to abstract this in a certain way, but I
really wonder if it's worth the effort. And there's more problems,
consider this:
- either you simply want to sum()/avg()/max()/etc. on a table and
its attributes, and then I guess it's probably sufficient to offer
the possibility to fetch(sql=3D'select max(age) from author'), as
stated above;
- or you want to use this along with the automatic generation of
complex queries (e.g. with multiple joins): okay, but then you
must be able to say which attributes you want, and it's definitely
not sufficient to tell which table it belongs to: relations can be
reflexive (such as the 'pygmalion' relationship in the author/book
model), and in such cases you have two possibilities for
Author.age: table alias t1 or t2 (referring to the sql statements
above). This also means that this expression needs to be bound to
the automatic generation of sql statements.
I can't think of a straightforward way to do this by now. Again, I'm
not saying this is impossible: I'm just playing with the interesting
idea and explaining the difficulties I can foresee, wondering whether
such an advanced functionality would be worth the effort. That's an
open question, and for the moment being I suggest we do not take this
into account _as far as the API change proposal is concerned_. This
could be discussed in a separate thread, and it would help a lot if we
had some real-life examples showing where this could be very handy.
--> Same for 'group by' and 'having' statements by the way, since I'm not
really familiar with them either.
Mario> I would also add "by indicating clearly the small subset of methods
Mario> intended for use by client code, and the stability level".
Right. I'm still looking for a way to include this in the docstrings
so that the generation of the API can eat it (FYI we use epydoc).
-- S=E9bastien.
|
|
From: Mario R. <ma...@ru...> - 2003-06-17 18:32:10
|
On jeudi, juin 12, 2003, at 16:34 Europe/Amsterdam, Sebastien Bigaret=20
wrote:
>
> Hi all,
>
> Some more comments about the API change proposal; I concentrate here =
on
> EditingContext, and will post another message for CustomObject and
> related APIs.
>
> Note: I also received two private answers.
>
> * Again: methods will be aliased, not replaced,
>
> * EC.insert() and EC.delete() are okay to everyone.
>
> * autoInsertion()/setAutoInsertion(): the former is a getter, the
> latter, a setter
>
> Mario> OK. What about commit() for saveChanges()?
>
> * about commit(): that was initially in my proposal. I removed
> it. Remember I'm working on integrating the ZODB transaction and
> observation mechanisms into the modeling? Well, then, commit()=20
> clashes
> with py2.1 zodb's transaction protocol (and has a very different
> semantic than saveChanges(): it is equivalent to =
objectWillChange()).
> Hence I'd prefer to let it apart from the proposal for the moment
> being.
OK.
> * EC.fetch():
>
> The initial proposal is accepted by everyone so we'll keep it as a=20=
> basis.
>
> Mario> Another issue is that even with all the keyword options=20
> offered, one
> Mario> still has to "dip into" small pieces of raw sql very quickly,=20=
> as shown
> Mario> by your examples.
>
> Here I strongly disagree with you. It /seems/ that you dip into =
small
> pieces of raw sql, but it's not the case at all. Qualifiers and
> FetchSpecification properties offers a generic command language;
> consider this:
>
> ec.fetch('Book', 'author.pygmalion.lastName ilike "r*"')
>
> -> this is far from the generated sql:
>
> SELECT t0.id, t0.title, t0.FK_WRITER_ID, t0.PRICE
> FROM BOOK t0
> INNER JOIN ( WRITER t1
> INNER JOIN WRITER t2
> ON t1.FK_WRITER_ID=3Dt2.ID )
> ON t0.FK_WRITER_ID=3Dt1.ID
> WHERE UPPER(t2.LAST_NAME) LIKE UPPER('r%');
>
> In the framework we generally make the strong assumption that raw =
sql
> is taken out of the python code. Even if some of the keywords (such=20=
> as
> 'like'/'ilike' for qualifiers, or 'asc'/'desc' for ordering) are
> actually the same than the corresponding sql keywords, they are=20
> indeed
> decorrelated. See again the example above: the 'ilike' keyword is=20=
> not
> used, rather we compare UPPER()s (this might change in the future,=20=
> now
> that 'ilike' is a sql keyword accepted by a lot of db-servers ;)
OK, sorry I am not appreciating enough the high-levelness of the query=20=
api...
Your example certainly is a nice generic separation, and simplification.
However by "bits" of sql i intended really little bits, such as the age=20=
param
and the orderBy param, below:
ec.fetch('Writer', 'age>=3D80"')
ec.fetch('Writer', orderBy=3D'firstName asc, lastName desc', isDeep=3D1)
>
> Mario> This may be an acceptable compromise, but then client code=20
> should be
> Mario> allowed to pass any SQL it wants...
>
> That's an other problem, to which I completely agree: we should have=20=
> a
> mean to execute complete raw sql statements, and get the (raw) =
result
> back. Moreover, we should be able to transform the returned raw rows
> into real objects if necessary (and if possible).
Very good. For "casting" a raw row to an object, probably a simple
generic utility function could be enough. The only problem is the
order of the tuple, and the mapping onto the attributes.
>> Another detail is that the object loads all (direct) properties, =
every
>> time -- there may be no workaround for this, given the way the
>> framework is built.
> (also related to your 'resultset' proposal)
>
> Last, we should also be able to tell the framework to return the raw
> rows instead of fully initialized objects (which can later be=20
> reverted
> to real objects): there's a real need for that; sometimes you want =
to
> present a (very) long list of objects in a summary page/widget, but
> you do not need the full objects, not even every attributes, but a
> subset to present the user. Then the users selects one or more of=20
> this
> rows and that's where you'll transform the raw rows to real objects.
>
> Note that the framework architecture will have no problem to support
> this. Some of the needed APIs are already present but not =
implemented
> (such as DatabaseContext.faultForRawRow()).
>
> I thought this was on the todo list but it's not --I'll add that,
> since I've been thinking about these points for quite a long time.
OK
> Impact on the API:
>
> when this is implemented I suggest we add the following parameters =
to
> fetch():
>
> rawRows -- (default: false) return raw rows instead of objects
>
> sql -- execute the sql statement (and ignore all other parameters
> except entityName and rawRows --both optional in that case)
Ok for rawRows on fetch().
However, come to think of it, sql should be part of a different=20
function,
with a different interface, and that always returns raw rows.
Something like fetchSql().
As it is very nice to have a full query interface that is completely
db independent, it is also convenient to be able to access specific
db query api features.
This could also supercede fetchCount() and other such
possibilities. In this way, fetch() can be guaranteed
to be db-independent, while fetchSql() may not be.
>
> BTW I also suggest that the unsupported features in the fetch API =
are
> removed until they are implemented (such as limit/page/offset/etc).
OK.
>
>> I feel that some things (even if no one has requested them yet ;) are
>> missing...
>> for example, should one ever need to take advantage of sql "group by"=20=
>> and
>> "having".
>> Access to these sql clauses may be added later, without breaking this=20=
>> API,
>> which is OK. Also, such manips may be done in the middle code, but
>> that would be very inefficient.
> [...]
>>
>> The real problem with this is that to request the result of an sql=20
>> function,
>> of which count() is an example, additional api functionality is=20
>> needed.
>> But what happens if I want other functions, such as SUM or MAX or=20
>> AVERAGE
>> over a few columns? Each of these functions may take complex=20
>> parameters.
>> Again, the functionality may be replicated in the middle code, but=20
>> this would
>> not only be a waste of development time, but also be very =
inefficient.
>>
>> I propose either a generalization of "select" (which may be too=20
>> complicated
>> in its implications), or an addition of a func keyword option, e.g.
>>
>> func=3D'count'
>> func=3D('funcName', param1, param2, ...)
>> func=3D( ('funcName', param1, param2, ...), ('funcname2') )
>>
>> func=3D('sum','age')
>>
>> The question is then how is this information returned?
>> I suggest as a tuple...
> [snipped]
>
> That's a very interesting idea, but this will take too much efforts
> for it to be shortly developped. Let me explain that: if we do this
> just as it sounds, then we will have *pieces* of raw sql in the=20
> middle
> of generic command patterns --I don't like that. Do not =
misunderstand
> me: it should be possible to abstract this in a certain way, but I
> really wonder if it's worth the effort.
This is certainly not high priority. It is a good point to consider
now to know that you will not be stuck with a limited API.
> And there's more problems,
> consider this:
>
> - either you simply want to sum()/avg()/max()/etc. on a table and
> its attributes, and then I guess it's probably sufficient to=20
> offer
> the possibility to fetch(sql=3D'select max(age) from author'), =
as
> stated above;
>
> - or you want to use this along with the automatic generation of
> complex queries (e.g. with multiple joins): okay, but then you
> must be able to say which attributes you want, and it's=20
> definitely
> not sufficient to tell which table it belongs to: relations can=20=
> be
> reflexive (such as the 'pygmalion' relationship in the=20
> author/book
> model), and in such cases you have two possibilities for
> Author.age: table alias t1 or t2 (referring to the sql =
statements
> above). This also means that this expression needs to be bound =
to
> the automatic generation of sql statements.
Yes, this second one is certainly difficult. For now, separation of
possibility to make raw sql fetches should be enough.
> I can't think of a straightforward way to do this by now. Again, I'm
> not saying this is impossible: I'm just playing with the interesting
> idea and explaining the difficulties I can foresee, wondering =
whether
> such an advanced functionality would be worth the effort. That's an
> open question, and for the moment being I suggest we do not take =
this
> into account _as far as the API change proposal is concerned_. This
> could be discussed in a separate thread, and it would help a lot if=20=
> we
> had some real-life examples showing where this could be very handy.
> --> Same for 'group by' and 'having' statements by the way, since=20
> I'm not
> really familiar with them either.
>
>
> Mario> I would also add "by indicating clearly the small subset of=20
> methods
> Mario> intended for use by client code, and the stability level".
>
> Right. I'm still looking for a way to include this in the docstrings
> so that the generation of the API can eat it (FYI we use epydoc).
OK, great.
mario
> -- S=E9bastien.
|
|
From: Sebastien B. <sbi...@us...> - 2003-06-12 16:22:55
|
And now for CustomObject and related APIs!
I've thought about it quite a lot since the first proposal. Answers I
got, including two private ones, helped me a lot. Let me summarize all
this.
* Backward compatibility is of great concern. This means in particular
that KeyValueCoding will remain a mix-in for CustomObject, and that
KeyValueCoding will almost remain as-is. I say almost, because we
should deprecate (not remove) the methods: setValueForKey(),
setValueForKeyPath() and setStoredValueForKey(). As Mario already
wrote in a previous post, their only presence makes it harder to
understand the API and I fully agree.
I don't remember when I added this but this was not a brilliant idea.
Deprecation: means that they will issue a warning (python module
warnings) when they are used, and they will be removed at a given
release, to be determined (after 0.9). Note that the framework does
not use them at all.
What do you think?
* I think I made a big mistake when exposing the KeyValueCoding so early
in the User's Guide --again, Mario already pointed that out. This part
should be moved in an 'advanced techniques' part in the guide. I
suspect that this is /one/ of the main reasons why people tends to
focus on this api, despite the fact that it is definitely not needed
at first sight.
* What about the new API: I've come to the (still-to-be-discussed)
conclusion that it should not be mixed-in to CustomObject. While
making aliases in the framework's core is no problem, adding more
methods to CustomObject can induce some problem because we would then
add methods to the users' classes (this was pointed out by one of the
private mail I got). I tend to agree with that.
However what we can do is make an alternate module offering both the
new methods and a mix-in class that people could use in their own
classes when they prefer it. It seems that this solution addresses the
request for changing the name while preventing the set of users
classes' methods to grow unwillingly with aliased methods (no
functionality added).
* Last, I must say that the other private msg I got supports the KVC and
considers it better than the new props. As far as I'm concerned, I'm
not opposed to aliases, but I must admit that my preference goes to
the original KVC api, well, just like any former NeXTStep or EOF
developper I guess ;)
CustomObject
------------
We'll add globalID() in CustomObject if there is no argument
against. That would be a handy shortcut for:
obj.editingContext().globalIDForObject(self)
which is a common idiom for people willing to access the primary keys'
values while not making them class properties (this question is a
recurrent one)
Validation
----------
> > Do we need to change anything there? validateValueForKey() is the
> > longest one, however I think this is not widely used, is it?
>=20
> I suggest validateProperty() instead of validateValueForKey().
Here again, backward compatibility means that the old name will be
kept. Again, this adds an alias and no functionality to
CustomObject. Moreover, I suspect this is all but a commonly used
method. I hate saying that but I feel like going one step backward and
remove the proposal. Please shout at me if you disagree ;)
Back on the new KVC:
--------------------
Module's name: I'd strongly prefer if it could be explicit rather than
an acronym (opr). GenericAccess still has my preference but that's
no strong pref., I'll probably never use it so basically _you_ have
the lead!
Methods names: I'm okay to use the full names, but then they will
sensibly be of the same length than the original ones, compare:
co.getProperty()
--valueForKey--
co.setProperty()
--setValueForKey--
co.getStoredProperty()
--storedValueForKey--
co.setStoredProperty()
--setStoredValueForKey--
co.updateProperties()
--takeValuesFromDictionary--
co.updateStoredProperties()
--takeStoredValuesFromDictionary--
Up to you, however.
BTW I'll probably extend KVC with valuesForKeys(), counterpart for
takeValueForKey(). I was surprised to realize it was not there.
This also means to add something like 'properties()' to the new API
(which is maybe problematic, I've the feeling that this particular
name could easily clash with another one defined elsewhere).
However *if* we agree on what I said above, i.e. that the new KVC will
not be a mix-in for CustomObject by default, then choosing the new api
can be decorrelated to the changes made to the core. These changes
would then only be:
+ CustomObject.globalID()
+ KeyValueCoding.
(plus the changes made to EditingContext)
What do you think?
With best regards,
-- S=E9bastien.
|
|
From: Ernesto R. <er...@si...> - 2003-06-12 20:12:25
|
Don't take me too serious, I'm new to this list. I agree that, especially for summaries, like statistics (counts, sums, average, etc.) raw SQL access may be needed, I saw direct SQL calls in some of existing products. But they return raw rows, i.e. an array. No middletier functionality. On the otherhand, it may be interesting to encapsulate this for later enhacenments. In one of the papers I studied on this theme (http://www.ambysoft.com/persistenceLayer.pdf, pg 7, point 8.) I saw the use of proxy objects which are light weight objects with the most basic attributes (some user-key, name and few more). This idea could be extended, telling the fetch machinery to return objects with some additional, user requested attributes. On the other hand, we could have summary objects, because it's very usual to get sums, counts and averages. The idea is that the resultset would be an encapsulated a collection of summary objects: objects=ec.fetchSummary(sourceEntityName="Book", what="count(*), avg(price), sum(toInvoiceLines.amount)", where="author.name like 'John*'", groupBy="author, genre", resultEntityName="BookSummary") This may seem to be a SQL (OQL is very near to), but it has advantages over SQL: * the dotted notation is allowed (get away from ugly inner joins). * it would process inheritance trees * don't have to repeat the grouping fields in select clause, they are added automatically The only purpose of the entityName is to give an 'entry entity' which the attributes are related to. The result could be just an instance of 'SummaryObject', or perhaps an instance of a special BookSummary which encapsulates special behaviour. Should the result be a flat list or a tree-like list or dictionary? Erny |
|
From: Sebastien B. <sbi...@us...> - 2003-06-12 23:56:38
|
"Ernesto Revilla" <er...@si...> wrote: > Don't take me too serious, I'm new to this list. >=20 > I agree that, especially for summaries, like statistics (counts, sums, > average, etc.) raw SQL access may be needed, I saw direct SQL calls in so= me > of existing products. But they return raw rows, i.e. an array. No middlet= ier > functionality. On the otherhand, it may be interesting to encapsulate this > for later enhacenments. >=20 > In one of the papers I studied on this theme > (http://www.ambysoft.com/persistenceLayer.pdf, pg 7, point 8.) I saw the = use > of proxy objects which are light weight objects with the most basic > attributes (some user-key, name and few more). This idea could be extende= d, > telling the fetch machinery to return objects with some additional, user > requested attributes. The idea of proxies is quite interesting and notably pretty straightforward to implement. Suppose the ability to fetch raw rows is implemented, the common idiom would be (not speaking about the fetch() shortcut) something like this: >>> qual=3DqualifierWithQualifierFormat('title ilike "%t%"') >>> fs=3DFetchSpecification('Book', qual) >>> fs.setFetchesRawRows(true) >>> fs.setRawRowsKeyPath('id', 'title', 'author.lastName') >>> rows=3Dec.objectsWithFetchSpecification(fs) >>> print rows ({'id': 1, 'title': 'Gargantua', 'author_lastName': 'Rabelais'}, {'id': 2, 'title': 'Bouge ton pied...', 'author_lastName': 'Dard'}, {'id': 4, 'title': "T'assieds pas...", 'author_lastName': 'Dard'}, ) (note: I consider that dicts are better than arrays as a result set) Then it simply a matter of declaring:=20 >>> class GenericProxy: pass after which the creation and initialization of the proxies is just: >>> proxies=3D[GenericProxy() for i in range(len(rows))] >>> [proxies[i].__dict__.update(rows[i]) for i in range(len(rows))] That's all! And that's a really nice idea. > On the other hand, we could have summary objects, because it's very usual= to > get sums, counts and averages. The idea is that the resultset would be an > encapsulated a collection of summary objects: >=20 > objects=3Dec.fetchSummary(sourceEntityName=3D"Book", what=3D"count(*), av= g(price), > sum(toInvoiceLines.amount)", where=3D"author.name like 'John*'", > groupBy=3D"author, genre", resultEntityName=3D"BookSummary") >=20 > This may seem to be a SQL (OQL is very near to), but it has advantages > over SQL: > * the dotted notation is allowed (get away from ugly inner joins). > * it would process inheritance trees > * don't have to repeat the grouping fields in select clause, they are add= ed > automatically > The only purpose of the entityName is to give an 'entry entity' which the > attributes are related to. The result could be just an instance of > 'SummaryObject', or perhaps an instance of a special BookSummary which > encapsulates special behaviour. * You make me realize that my previous answer to Mario saying ''you'll have to be able to distinguish between table alias t1 and table alias t2 when declaring attribute author.age and that's a problem bla bla bla'' was stupid: the dotted notation obviously solves this naturally (the distinction between author.age and author.pygmalion.age is clear). * You're partially answering to the question of real-life examples --although I still need to familiarize myself with 'group by' and 'having' statements. * This brings a different light to what Mario formerly proposed (different api but the very same idea as far as I can see) --just because we here use a different method (fetchSummmary instead of fetch); sorry, sometimes I can't see the forest for the trees because of such details :| Now that the concept (summaries) is clearer to me I'm beginning to see the ideas behind. And I guess I'm not far from thinking that, okay, fetchSummary() does contain raw sql pieces, but that it's maybe not such a big deal if this covers common needs. * could this be made clearer: despite the fact that I cannot really see its implication for the moment being, I have the feeling that 'group by' and multiple fetches (walking through the inheritance tree) is not only a matter of getting the results and assembling them one after the other, is it? > Should the result be a flat list or a tree-like list or dictionary? I cannot answer that: as I said I'm not familiar enough with this kind of queries to see what form would be the most accurate, or whether it could depend on the query itself. I'd need different examples and your opinion here. BTW I suggest that we do not consider this as an element of the current API change proposal, rather as a new feature request. Last, you wrote: > On the other hand, we could have summary objects, because it's very > usual to get sums, counts and averages. I'd like to hear more about those usual cases, if you could give some examples that would help --help me, at least ;) since my personal approach to that kind of things would be e.g. to treat the raw rows on the python side rather than to ask the rdbms to do the job. -- S=E9bastien. |
|
From: Ernesto R. <er...@si...> - 2003-06-13 01:18:58
|
Examples for totals (sums) and summaries:
As we study economic data to decide if a company goes right, here are some
cases:
* Total revenues by month (group by year-month)
select month(year) as mes, sum(totalAmount) where date between
'2003-01-01' and '2003-12-31'
group by month (date)
for more than a year:
select year(date) as ano, month(year) as mes, sum(totalAmount) where
date between '2003-01-01' and '2003-12-31'
group by year(month), month(date)
The database does this in 2 seconds for 20.000 records
* costs, revenues and difference (benefit) by month
* customers who bought prodcuts with a total amount more than X (send them
some gifts)
select customer,sum(totalAmount) as Total from invoices where date
between.... group by customer having total>10.000
* costs to produce each order of the month (hours, materials, etc.)
* we use them a lot if we have to compare numbers as a whole or in groups,
for example, accounting with invoices (because invoice generate assets, but
user change invoices after assets have been done, and elsewhere change any
another data, so we check if the accounting is more or less ok, so the
company can pay VAT to the government.)
* consistency checks, e.g. see if anybody really has one address:
select person.id from person inner join address on
person.id=address.FK_person group by person.id having count(*)>1
* best-sellers:
select article,count(*) As numberSold from productLine group by article
having numberSold>20.000
Ok this could all be done in Python, but I think this is a bit slower. One
of our customers has something like 50.000 delivery notes a year, and this
is not much as bigger companies (we only have 'small' customers) can have
hundreds of thousands of documents a year. It's amazing how fast some
databases are. They only take 2 or 3 seconds to answer those questions. I
think Python takes at least 10 times more and consumes a lot of memory.
The implication on vertical inheritence mapping is that we have to retrieve
subtotals for each table and total them in python at the final step.
Although there are other operations like average, max, min, etc. but these
are easy done. To really get knowledge out of the data it doesn't make sense
that the result is too big, because we are only human. Perhaps 100-200 rows
max? So if each table summary is 100 records, we have to sum them for each
class of the inheritance tree, so we would have to process <1000 numbers in
python which is fast enough. (In real-life queries, we'll have often queries
much smaller with <100 numbers to process.)
The idea is that the query has not raw sql, as we need to process the
summary operations at both levels, first SQL than Python. The 'having'
operation should also be done at both levels (so db returns less data, and
Python only has to filter out the ultimate result.) Although 'where'
condition could be raw, Modeling already processes it the right way, so
nothing has to be done.
The SQL 'GROUP BY' returns a tuple for each combination of the the grouping
values, but it really expresses a tree (list of lists, or dictionary of
dictionaries): e.g. how many products of each has bought every customer on a
per month basis?
cust1 product1 month1 100
cust1 product1 month2 130
cust1 product1 month4 90 # 0 for month3 doesn't appear using
SQL
....
cust1 product2 month1 150
cust1 product2 month7 130
cust1 product3 month2 10
....
cust4 product1 month3 10
....
If this was a multi-dimensional structure (customer, product, date, units
bought), every summary is a projection into a space with fewer dimensions.
This is just was OLAP and data-warehounsing does: it picks up data from
several tables, several databases, etc. and puts it into a multidimensional
structure (getting effectively hypercubes) and throws projections to answer
business strategic questions.
This is not for the 0.9 release (nor perhaps for the 1.0 release, I don't
know). Better a bit slower but more solid. (We'll need at least one year
more for our software, although we have to pick up all the basic components
now: PyGTK & XML-RPC (client) - Modeling & Webware or Zope / XML-RPC
(middle-tier) - postgres (back-end) - reportlab (pdf - generation) -
cheetah, etc.)
Erny
----- Original Message -----
From: "Sebastien Bigaret" <sbi...@us...>
To: "modeling-users" <mod...@li...>
Sent: Friday, June 13, 2003 1:55 AM
Subject: Re: [Modeling-users] Summary objects and Proxy objects
"Ernesto Revilla" <er...@si...> wrote:
> Don't take me too serious, I'm new to this list.
>
> I agree that, especially for summaries, like statistics (counts, sums,
> average, etc.) raw SQL access may be needed, I saw direct SQL calls in
some
> of existing products. But they return raw rows, i.e. an array. No
middletier
> functionality. On the otherhand, it may be interesting to encapsulate this
> for later enhacenments.
>
> In one of the papers I studied on this theme
> (http://www.ambysoft.com/persistenceLayer.pdf, pg 7, point 8.) I saw the
use
> of proxy objects which are light weight objects with the most basic
> attributes (some user-key, name and few more). This idea could be
extended,
> telling the fetch machinery to return objects with some additional, user
> requested attributes.
The idea of proxies is quite interesting and notably pretty
straightforward to implement. Suppose the ability to fetch raw rows is
implemented, the common idiom would be (not speaking about the fetch()
shortcut) something like this:
>>> qual=qualifierWithQualifierFormat('title ilike "%t%"')
>>> fs=FetchSpecification('Book', qual)
>>> fs.setFetchesRawRows(true)
>>> fs.setRawRowsKeyPath('id', 'title', 'author.lastName')
>>> rows=ec.objectsWithFetchSpecification(fs)
>>> print rows
({'id': 1, 'title': 'Gargantua', 'author_lastName': 'Rabelais'},
{'id': 2, 'title': 'Bouge ton pied...', 'author_lastName': 'Dard'},
{'id': 4, 'title': "T'assieds pas...", 'author_lastName': 'Dard'},
)
(note: I consider that dicts are better than arrays as a result set)
Then it simply a matter of declaring:
>>> class GenericProxy: pass
after which the creation and initialization of the proxies is just:
>>> proxies=[GenericProxy() for i in range(len(rows))]
>>> [proxies[i].__dict__.update(rows[i]) for i in range(len(rows))]
That's all!
And that's a really nice idea.
> On the other hand, we could have summary objects, because it's very usual
to
> get sums, counts and averages. The idea is that the resultset would be an
> encapsulated a collection of summary objects:
>
> objects=ec.fetchSummary(sourceEntityName="Book", what="count(*),
avg(price),
> sum(toInvoiceLines.amount)", where="author.name like 'John*'",
> groupBy="author, genre", resultEntityName="BookSummary")
>
> This may seem to be a SQL (OQL is very near to), but it has advantages
> over SQL:
> * the dotted notation is allowed (get away from ugly inner joins).
> * it would process inheritance trees
> * don't have to repeat the grouping fields in select clause, they are
added
> automatically
> The only purpose of the entityName is to give an 'entry entity' which the
> attributes are related to. The result could be just an instance of
> 'SummaryObject', or perhaps an instance of a special BookSummary which
> encapsulates special behaviour.
* You make me realize that my previous answer to Mario saying ''you'll
have to be able to distinguish between table alias t1 and table alias
t2 when declaring attribute author.age and that's a problem bla bla
bla'' was stupid: the dotted notation obviously solves this naturally
(the distinction between author.age and author.pygmalion.age is
clear).
* You're partially answering to the question of real-life examples
--although I still need to familiarize myself with 'group by' and
'having' statements.
* This brings a different light to what Mario formerly proposed
(different api but the very same idea as far as I can see) --just
because we here use a different method (fetchSummmary instead of
fetch); sorry, sometimes I can't see the forest for the trees because
of such details :|
Now that the concept (summaries) is clearer to me I'm beginning to see
the ideas behind. And I guess I'm not far from thinking that, okay,
fetchSummary() does contain raw sql pieces, but that it's maybe not
such a big deal if this covers common needs.
* could this be made clearer: despite the fact that I cannot really see
its implication for the moment being, I have the feeling that 'group
by' and multiple fetches (walking through the inheritance tree) is not
only a matter of getting the results and assembling them one after the
other, is it?
> Should the result be a flat list or a tree-like list or dictionary?
I cannot answer that: as I said I'm not familiar enough with this kind
of queries to see what form would be the most accurate, or whether it
could depend on the query itself. I'd need different examples and your
opinion here.
BTW I suggest that we do not consider this as an element of the
current API change proposal, rather as a new feature request.
Last, you wrote:
> On the other hand, we could have summary objects, because it's very
> usual to get sums, counts and averages.
I'd like to hear more about those usual cases, if you could give some
examples that would help --help me, at least ;) since my personal
approach to that kind of things would be e.g. to treat the raw rows
on the python side rather than to ask the rdbms to do the job.
-- Sébastien.
-------------------------------------------------------
This SF.NET email is sponsored by: eBay
Great deals on office technology -- on eBay now! Click here:
http://adfarm.mediaplex.com/ad/ck/711-11697-6916-5
_______________________________________________
Modeling-users mailing list
Mod...@li...
https://lists.sourceforge.net/lists/listinfo/modeling-users
|
|
From: Sebastien B. <sbi...@us...> - 2003-06-12 16:24:32
|
And now for CustomObject and related APIs!
I've thought about it quite a lot since the first proposal. Answers I
got, including two private ones, helped me a lot. Let me summarize all
this.
* Backward compatibility is of great concern. This means in particular
that KeyValueCoding will remain a mix-in for CustomObject, and that
KeyValueCoding will almost remain as-is. I say almost, because we
should deprecate (not remove) the methods: setValueForKey(),
setValueForKeyPath() and setStoredValueForKey(). As Mario already
wrote in a previous post, their only presence makes it harder to
understand the API and I fully agree.
I don't remember when I added this but this was not a brilliant idea.
Deprecation: means that they will issue a warning (python module
warnings) when they are used, and they will be removed at a given
release, to be determined (after 0.9). Note that the framework does
not use them at all.
What do you think?
* I think I made a big mistake when exposing the KeyValueCoding so early
in the User's Guide --again, Mario already pointed that out. This part
should be moved in an 'advanced techniques' part in the guide. I
suspect that this is /one/ of the main reasons why people tends to
focus on this api, despite the fact that it is definitely not needed
at first sight.
* What about the new API: I've come to the (still-to-be-discussed)
conclusion that it should not be mixed-in to CustomObject. While
making aliases in the framework's core is no problem, adding more
methods to CustomObject can induce some problem because we would then
add methods to the users' classes (this was pointed out by one of the
private mail I got). I tend to agree with that.
However what we can do is make an alternate module offering both the
new methods and a mix-in class that people could use in their own
classes when they prefer it. It seems that this solution addresses the
request for changing the name while preventing the set of users
classes' methods to grow unwillingly with aliased methods (no
functionality added).
* Last, I must say that the other private msg I got supports the KVC and
considers it better than the new props. As far as I'm concerned, I'm
not opposed to aliases, but I must admit that my preference goes to
the original KVC api, well, just like any former NeXTStep or EOF
developper I guess ;)
CustomObject
------------
We'll add globalID() in CustomObject if there is no argument
against. That would be a handy shortcut for:
obj.editingContext().globalIDForObject(self)
which is a common idiom for people willing to access the primary keys'
values while not making them class properties (this question is a
recurrent one)
Validation
----------
> > Do we need to change anything there? validateValueForKey() is the
> > longest one, however I think this is not widely used, is it?
>=20
> I suggest validateProperty() instead of validateValueForKey().
Here again, backward compatibility means that the old name will be
kept. Again, this adds an alias and no functionality to
CustomObject. Moreover, I suspect this is all but a commonly used
method. I hate saying that but I feel like going one step backward and
remove the proposal. Please shout at me if you disagree ;)
Back on the new KVC:
--------------------
Module's name: I'd strongly prefer if it could be explicit rather than
an acronym (opr). GenericAccess still has my preference but that's
no strong pref., I'll probably never use it so basically _you_ have
the lead!
Methods names: I'm okay to use the full names, but then they will
sensibly be of the same length than the original ones, compare:
co.getProperty()
--valueForKey--
co.setProperty()
--setValueForKey--
co.getStoredProperty()
--storedValueForKey--
co.setStoredProperty()
--setStoredValueForKey--
co.updateProperties()
--takeValuesFromDictionary--
co.updateStoredProperties()
--takeStoredValuesFromDictionary--
Up to you, however.
BTW I'll probably extend KVC with valuesForKeys(), counterpart for
takeValueForKey(). I was surprised to realize it was not there.
This also means to add something like 'properties()' to the new API
(which is maybe problematic, I've the feeling that this particular
name could easily clash with another one defined elsewhere).
However *if* we agree on what I said above, i.e. that the new KVC will
not be a mix-in for CustomObject by default, then choosing the new api
can be decorrelated to the changes made to the core. These changes
would then only be:
+ CustomObject.globalID()
+ KeyValueCoding.
(plus the changes made to EditingContext)
What do you think?
-- S=E9bastien.
|
|
From: Sebastien B. <sbi...@us...> - 2003-06-24 09:44:36
|
Hi,
Time to summarize the changes we discussed here:
EditingContext
--------------
- insert() is an alias for insertObject()
- delete() is an alias for deleteObject()
- fetching: remove the need to import FetchSpecification and
Qualifier, and propose an alternate for
objectsWithFetchSpecification()
def fetch(self, entityName,
qualifier=3DNone, # either a Qualifier instance or a string
isDeep=3D0, # should subentities be fetched as well?
)
Note that parameters orderBy, limit/page/offset, lock and rawRows
have been removed since they are unsupported yet (they will be
introduced when support is available).
- make fetchCount() an alias for objectsCountWithFetchSpecification()
[same API as fetch()]
- make (set)autoInsertion() aliases for
(set)propagatesInsertionForRelatedObjects()
=20=20=20=20
CustomObject
------------
- add globalID()
KeyValueCoding
--------------
- deprecate methods setValueForKey(), setValueForKeyPath() and
setStoredValueForKey()
I propose to set the removal time for these deprecated methods at
version 0.9.1
- add valuesForKeys(), counterpart for takeValuesFromDictionary()
Additionally, I propose to move the chapter dealing with KVC to an
other part in the User's Guide, in some 'advanced techniques' chapter,
so that it is not exposed as it is today.
Validation
----------
No changes (because of backward compatibility issues, mainly)
About the ``new'' KVC module:
-----------------------------
Do we agree that it can be separatedly defined so that users can use
it as a mix-in for their classes, at their will? If so, does anyone
want to take the lead for this? (meaning at least continuing the
discussion and coming to a decision)
Future enhancements
-------------------
Interesting future features (NOT included in this proposal) have been
proposed in this thread, including:
- fetchSQL(): the possibility to pass a raw sql query to build
objects,
- the ability to fetch raw rows (i.e. raw dict) rather than
fully-initialized objects,
- fetchSummary(): adds access to sum()/avg()/... and group by/having
to the fetching API
We'll probably need to discuss some of these points a bit more,
however if you think you'll need one or more of these for the coming
releases please fill in a RFE on sourceforge's page and announce it
here --this is definitely the best way to make it happen sooner.
I'd like to validate the API change proposal for the end of the week, if
possible, so I'd appreciate if you could comment on this in the coming
days. And BTW thanks a lot for the attention you already paid to this
topic.
-- S=E9bastien.
|
|
From: Sebastien B. <sbi...@us...> - 2003-07-01 19:21:21
|
Hi all,
Got no answer for this; I guess this has already been enough
discussed. Since the proposed changes is a subset of the initial one
that everybody aggreed on, I'll apply them on thursday evening (ie in
two days) --unless somebody comments further on this.
I'll then make a new release.
-- S=E9bastien.
Sebastien Bigaret <sbi...@us...> writes:
> Hi,
>=20
> Time to summarize the changes we discussed here:
>=20
> EditingContext
> --------------
>=20
> - insert() is an alias for insertObject()
>=20
> - delete() is an alias for deleteObject()
>=20
> - fetching: remove the need to import FetchSpecification and
> Qualifier, and propose an alternate for
> objectsWithFetchSpecification()
>=20
> def fetch(self, entityName,
> qualifier=3DNone, # either a Qualifier instance or a str=
ing
> isDeep=3D0, # should subentities be fetched as wel=
l?
> )
>=20
> Note that parameters orderBy, limit/page/offset, lock and rawRows
> have been removed since they are unsupported yet (they will be
> introduced when support is available).
>=20
> - make fetchCount() an alias for objectsCountWithFetchSpecification()
> [same API as fetch()]
>=20
> - make (set)autoInsertion() aliases for
> (set)propagatesInsertionForRelatedObjects()
>=20=20=20=20=20
>=20
> CustomObject
> ------------
>=20
> - add globalID()
>=20
> KeyValueCoding
> --------------
>=20
> - deprecate methods setValueForKey(), setValueForKeyPath() and
> setStoredValueForKey()
>=20
> I propose to set the removal time for these deprecated methods at
> version 0.9.1
>=20
> - add valuesForKeys(), counterpart for takeValuesFromDictionary()
>=20
> Additionally, I propose to move the chapter dealing with KVC to an
> other part in the User's Guide, in some 'advanced techniques' chapter,
> so that it is not exposed as it is today.
>=20
> Validation
> ----------
>=20
> No changes (because of backward compatibility issues, mainly)
>=20
>=20
> About the ``new'' KVC module:
> -----------------------------
>=20
> Do we agree that it can be separatedly defined so that users can use
> it as a mix-in for their classes, at their will? If so, does anyone
> want to take the lead for this? (meaning at least continuing the
> discussion and coming to a decision)
>=20
>=20
> Future enhancements
> -------------------
>=20
> Interesting future features (NOT included in this proposal) have been
> proposed in this thread, including:
>=20
> - fetchSQL(): the possibility to pass a raw sql query to build
> objects,
>=20
> - the ability to fetch raw rows (i.e. raw dict) rather than
> fully-initialized objects,
>=20
> - fetchSummary(): adds access to sum()/avg()/... and group by/having
> to the fetching API
>=20
> We'll probably need to discuss some of these points a bit more,
> however if you think you'll need one or more of these for the coming
> releases please fill in a RFE on sourceforge's page and announce it
> here --this is definitely the best way to make it happen sooner.
>=20
>=20
> I'd like to validate the API change proposal for the end of the week, if
> possible, so I'd appreciate if you could comment on this in the coming
> days. And BTW thanks a lot for the attention you already paid to this
> topic.
>=20
>=20
> -- S=E9bastien.
>=20
>=20
>=20
> -------------------------------------------------------
> This SF.Net email is sponsored by: INetU
> Attention Web Developers & Consultants: Become An INetU Hosting Partner.
> Refer Dedicated Servers. We Manage Them. You Get 10% Monthly Commission!
> INetU Dedicated Managed Hosting http://www.inetu.net/partner/index.php
> _______________________________________________
> Modeling-users mailing list
> Mod...@li...
> https://lists.sourceforge.net/lists/listinfo/modeling-users
|
|
From: Mario R. <ma...@ru...> - 2003-07-02 08:06:12
|
Hi,
sorry about this reply being so late... I agree with most of
what is summarized below, but have a few comments.
A general comment is about the "new" method being an alias
for the "old" method (this may only be a lingo issue). It seems
sensible to me to give the "new" or preferred method the
code, and other methods that need to be supported for
bw compatibility are the aliases (plus an appropriate docstring).
This also helps clarify which methods are to be standardized on.
On mardi, juin 24, 2003, at 11:43 Europe/Amsterdam, Sebastien Bigaret=20
wrote:
>
> Hi,
>
> Time to summarize the changes we discussed here:
>
> EditingContext
> --------------
>
> - insert() is an alias for insertObject()
>
> - delete() is an alias for deleteObject()
>
> - fetching: remove the need to import FetchSpecification and
> Qualifier, and propose an alternate for
> objectsWithFetchSpecification()
>
> def fetch(self, entityName,
> qualifier=3DNone, # either a Qualifier instance or a=20=
> string
> isDeep=3D0, # should subentities be fetched as=20=
> well?
> )
>
> Note that parameters orderBy, limit/page/offset, lock and rawRows
> have been removed since they are unsupported yet (they will be
> introduced when support is available).
OK.
> - make fetchCount() an alias for =
objectsCountWithFetchSpecification()
> [same API as fetch()]
Hmmn, I would not introduce such a specialized method (will become
clutter even in the short term). As suggested earlier, it may be more
flexible to just have a fetchSql() open method for now (that is not
necessarily db-independent) and that returns whatever the dbapi
returns. For me nothing is gained by having fetchCount('customobjname')
over fetchSql('select count(*) from tblname') -- it is jn fact less=20
flexibe.
When the time comes to generailize this to be db-independant, either
the fetch() method will gain new parameters to allow this, or other
generic methods introduced, such as fetchRaw, fetchTuples, fetchDicts,
or whatever. fetchSql can always have a use, though, so it it not likely
to become clutter.
> - make (set)autoInsertion() aliases for
> =
(set)propagatesInsertionForRelatedObjects()
OK
> CustomObject
> ------------
>
> - add globalID()
Yes. The scope of gloabl here is the EditingContext (and children)
or all EditingContexts in this python session, or the current model,
the db, ...
>
> KeyValueCoding
> --------------
>
> - deprecate methods setValueForKey(), setValueForKeyPath() and
> setStoredValueForKey()
>
> I propose to set the removal time for these deprecated methods at
> version 0.9.1
OK.
> - add valuesForKeys(), counterpart for takeValuesFromDictionary()
>
> Additionally, I propose to move the chapter dealing with KVC to an
> other part in the User's Guide, in some 'advanced techniques'=20
> chapter,
> so that it is not exposed as it is today.
OK, we can discuss this more (where to move this to in the guide).
> Validation
> ----------
>
> No changes (because of backward compatibility issues, mainly)
>
>
> About the ``new'' KVC module:
> -----------------------------
>
> Do we agree that it can be separatedly defined so that users can use
> it as a mix-in for their classes, at their will? If so, does anyone
> want to take the lead for this? (meaning at least continuing the
> discussion and coming to a decision)
I was under the impression that you said it will **not** be a mix-in=20
(as per:
http://sourceforge.net/mailarchive/message.php?msg_id=3D5250043 ).
The interface described in the archive message above is OK for me.
However, if it is to be a mix-in, discussion on this can continue=20
independently
of these changes? In any case, I will be happy to continue to be a part=20=
of
this discussion.
> Future enhancements
> -------------------
>
> Interesting future features (NOT included in this proposal) have =
been
> proposed in this thread, including:
>
> - fetchSQL(): the possibility to pass a raw sql query to build
> objects,
>
> - the ability to fetch raw rows (i.e. raw dict) rather than
> fully-initialized objects,
>
> - fetchSummary(): adds access to sum()/avg()/... and group=20
> by/having
> to the fetching API
>
> We'll probably need to discuss some of these points a bit more,
> however if you think you'll need one or more of these for the coming
> releases please fill in a RFE on sourceforge's page and announce it
> here --this is definitely the best way to make it happen sooner.
OK, except for fetchSQL. I think this should be not so difficult to add=20=
as
a generic method, and will take off the pressure on other premature
additions.
> I'd like to validate the API change proposal for the end of the week,=20=
> if
> possible, so I'd appreciate if you could comment on this in the coming
> days. And BTW thanks a lot for the attention you already paid to this
> topic.
>
>
> -- S=E9bastien.
Also, what about the renaming of =20
addObjectToBothSidesOfRelationshipWithKey()
and removeObjectFromBothSidesOfRelationshipWithKey(), respectively with
addViaRelation() and removeViaRelation() ?
And, yes I think RelationshipManipulation should be combined with KVC
(or whatever this is renamed to -- how about "Access" module
within a CustomObject sub-package ?)
mario
|
|
From: Jerome K. <Jer...@fi...> - 2003-07-02 09:22:39
|
Mario Ruggier wrote:
Hum .. A general note about aliasing methods: In fact
I use a couple of tools for test (SQLObject / MiddleKit/
and Modeling) so as MiddleKit and Modeling try to
monkey EOF, it use the same method names like
insertObject() .. so for me staying closer to EOF is a good
thing .
>> - make fetchCount() an alias for objectsCountWithFetchSpecification()
>> [same API as fetch()]
>
>
> Hmmn, I would not introduce such a specialized method (will become
> clutter even in the short term). As suggested earlier, it may be more
> flexible to just have a fetchSql() open method for now (that is not
> necessarily db-independent) and that returns whatever the dbapi
> returns. For me nothing is gained by having fetchCount('customobjname')
> over fetchSql('select count(*) from tblname') -- it is jn fact less
> flexibe.
> When the time comes to generailize this to be db-independant, either
> the fetch() method will gain new parameters to allow this, or other
> generic methods introduced, such as fetchRaw, fetchTuples, fetchDicts,
> or whatever. fetchSql can always have a use, though, so it it not likely
> to become clutter.
I guess you miss something Mario.
Let's take a example .
qualifier=qualifierWithQualifierFormat('lastName=="Hugo"')
fetchSpec=FetchSpecification(entityName='Writer', qualifier=qualifier)
fetchCount(fetchSpec) will return the number of writer with lastname =
'Hugo' ..
and so on this is not so trivial as a simple 'select count(*) ..'
so the answer is no : fetchCount() and fetchSql() haven't the same
purpose, even
is you can use fetSQL to do a fetchCount() or anything else.
And i really like the OO approach of modeling, and i really think that
even fetchSQL() ( which for me should be fetchSQLObject() ) should
alway return a object.
Take a look at this:
- in the model you describe the object mapping (which is not a real object
but only a "result object" .
for example:
CustomSearch
- searchString()
- books relation one-> many
- ...
- I thinks we should find a way to do
ec.objectsWithFetchSpecification('CustomSearch, ..searchString = 'Toto') ..
and so provide a method to do some special SQL in the CustomSearch() which
return CustomSearch object.
But this way you can do something like
search = CustomSearch(...)
books = search.getBooks() ( which return some books object .. not raw SQL)
>> Future enhancements
>> -------------------
>>
>> Interesting future features (NOT included in this proposal) have been
>> proposed in this thread, including:
>>
>> - fetchSQL(): the possibility to pass a raw sql query to build
>> objects,
>
To build Objects or raw SQL ?
Perhaps we can take a look on how SQLObject do that ?
>>
>> - the ability to fetch raw rows (i.e. raw dict) rather than
>> fully-initialized objects,
>>
yeah ..
Bye Bye ..
|
|
From: Mario R. <ma...@ru...> - 2003-07-02 09:35:03
|
On mercredi, juil 2, 2003, at 11:05 Europe/Amsterdam, Jerome Kerdreux
wrote:
> Mario Ruggier wrote:
>
>
> Hum .. A general note about aliasing methods: In fact
> I use a couple of tools for test (SQLObject / MiddleKit/
> and Modeling) so as MiddleKit and Modeling try to
> monkey EOF, it use the same method names like
> insertObject() .. so for me staying closer to EOF is a good
> thing .
That would be fine. My point was that whatever method
name is preferred, then this should be the "master" and
others that do same thing are aliases. This is irrespective of
whether the preferred method name is the same as in the
EOF or not.
>>> - make fetchCount() an alias for
>>> objectsCountWithFetchSpecification()
>>> [same API as fetch()]
>>
>>
>> Hmmn, I would not introduce such a specialized method (will become
>> clutter even in the short term). As suggested earlier, it may be more
>> flexible to just have a fetchSql() open method for now (that is not
>> necessarily db-independent) and that returns whatever the dbapi
>> returns. For me nothing is gained by having
>> fetchCount('customobjname')
>> over fetchSql('select count(*) from tblname') -- it is jn fact less
>> flexibe.
>> When the time comes to generailize this to be db-independant, either
>> the fetch() method will gain new parameters to allow this, or other
>> generic methods introduced, such as fetchRaw, fetchTuples, fetchDicts,
>> or whatever. fetchSql can always have a use, though, so it it not
>> likely
>> to become clutter.
>
>
> I guess you miss something Mario.
> Let's take a example .
> qualifier=qualifierWithQualifierFormat('lastName=="Hugo"')
> fetchSpec=FetchSpecification(entityName='Writer', qualifier=qualifier)
>
> fetchCount(fetchSpec) will return the number of writer with lastname =
> 'Hugo' ..
> and so on this is not so trivial as a simple 'select count(*) ..'
>
> so the answer is no : fetchCount() and fetchSql() haven't the same
> purpose, even
> is you can use fetSQL to do a fetchCount() or anything else.
Fair enough. fetchSQL is an open-ended convenience hack, that
allows more time to think over what a more generalized API for
fetch() could be -- so that it would handle in an OO way queries
such as qualified counts, sums, etc. I suspect that defining
a fetchCount will lead to other unnecessary definitions of other
"dedicated" methods (that could all be addressed with a well-thought
out, and simpler, fetch+ interface).
> And i really like the OO approach of modeling, and i really think that
> even fetchSQL() ( which for me should be fetchSQLObject() ) should
> alway return a object.
Yes, agreed. To achieve this, there needs to be a way to allow
"generic custom objects" to be returned by fetches, i.e. objects
that do not necessarily correspond to a class defined in a model
(such as for count, sum, ...), or, as you nicely describe below,
object that 'is not a real object but only a "result object"'.
>
> Take a look at this:
> - in the model you describe the object mapping (which is not a real
> object
> but only a "result object" . for example:
> CustomSearch
> - searchString()
> - books relation one-> many
> - ...
> - I thinks we should find a way to do
> ec.objectsWithFetchSpecification('CustomSearch, ..searchString =
> 'Toto') ..
> and so provide a method to do some special SQL in the CustomSearch()
> which
> return CustomSearch object.
>
> But this way you can do something like
> search = CustomSearch(...)
> books = search.getBooks() ( which return some books object .. not raw
> SQL)
Absolutely. It would be wonderful to have fetch API also be able
to handle this. The question is how to generalize the API to achieve
all these features (plus be easily extensible) while keeping it as
simple as possible.
mario
|
|
From: Sebastien B. <sbi...@us...> - 2003-07-02 21:59:03
|
Hi,
Mario Ruggier <ma...@ru...> wrote:
> On mercredi, juil 2, 2003, at 11:05 Europe/Amsterdam, Jerome Kerdreux wro=
te:
> > Mario Ruggier wrote:
> >
> > A general comment is about the "new" method being an alias for the "old"
> > method (this may only be a lingo issue). It seems sensible to me to give
> > the "new" or preferred method the code, and other methods that need to =
be
> > supported for bw compatibility are the aliases (plus an appropriate
> > docstring). This also helps clarify which methods are to be standardiz=
ed
> > on.
> > Hum .. A general note about aliasing methods: In fact
> > I use a couple of tools for test (SQLObject / MiddleKit/
> > and Modeling) so as MiddleKit and Modeling try to
> > monkey EOF, it use the same method names like
> > insertObject() .. so for me staying closer to EOF is a good
> > thing .
>=20
> That would be fine. My point was that whatever method
> name is preferred, then this should be the "master" and
> others that do same thing are aliases. This is irrespective of
> whether the preferred method name is the same as in the
> EOF or not.
I see your points here, both; I have no definitive answer, my point of
view is a bit different.
- real aliases, i.e. methods w/ different name but w/ the very same
functionality as the aliased methods (insert(), delete(),
setAutoInsertion), all these have in my mind the very same status.
In the code point of view, they'll be made by simple assignments:
'insert=3DinsertObject'; they'll actually *be* the very same method,
sharing code and docstrings.
In a developer's point of view, I guess the preference will go here
or there; if you're used to sql, 'insert' will probably be natural,
while 'insertObject' will come up more easily if you already know
SQLObject or the EOF.
So I won't say there will be a preference in itself between these,
only a personal preference. Even if they appear in different
circumstances, historical or at users' request, they are, and probably
will remain, identical.
- Framework's enhancements: fetch(), fetchCount()
These are not just aliases: you can think of the fa=E7ade design
pattern. They alleviate the developer's work by offering a simpler and
handier API to fetch objects.
Here again, I won't say that there will be a preferred one in the
absolute. fetch() is a real enhancement and I'm glad it's showing up,
because having to import modules, then instanciate a Qualifier and a
FetchSpecification just to fetch some objects is just more painful
than simply call fetch with the needed parameters.
However, the "old way" remains meaningful --and different, because
it offers plain control over the different objects collaborating
within the framework at some point (here, when fetching). It will
eventually be moved into an 'advanced techniques' section, of course,
and handier methods will be introduced first; after all, the framework
is supposed to make it easy to access to objects stored in a db!
An other slighty-different question to which Mario was probably
referring to is what are the methods you need to know at first sight,
and what are those that you can safely ignore if you do not want to
fine-tune the framework, or if you don't have time to dive too deeply
into it. This is an important issue I'm aware of. I already said here
that I'm looking for a solution to distinguish the one from the others
in the documentation. I haven't spent on it, still, but this *will* be
addressed in the future.
Sorry, I leave the other interesting points pending --mostly because
they are out of the scope of the actual API changes, and because I
need some rest now ;) But we'll get back to these for sure, and maybe
Soif could elaborate a bit further on his CustomSearch example? It
would be interesting to see how this compares to the other approaches
we already discussed here.
Cheers,
-- S=E9bastien.
|
|
From: Jerome K. <Jer...@fi...> - 2003-07-02 11:20:09
|
Mario Ruggier wrote:
Hum .. A general note about aliasing methods: In fact
I use a couple of tools for test (SQLObject / MiddleKit/
and Modeling) so as MiddleKit and Modeling try to
monkey EOF, it use the same method names like
insertObject() .. so for me staying closer to EOF is a good
thing .
>> - make fetchCount() an alias for objectsCountWithFetchSpecification()
>> [same API as fetch()]
>
>
> Hmmn, I would not introduce such a specialized method (will become
> clutter even in the short term). As suggested earlier, it may be more
> flexible to just have a fetchSql() open method for now (that is not
> necessarily db-independent) and that returns whatever the dbapi
> returns. For me nothing is gained by having fetchCount('customobjname')
> over fetchSql('select count(*) from tblname') -- it is jn fact less
> flexibe.
> When the time comes to generailize this to be db-independant, either
> the fetch() method will gain new parameters to allow this, or other
> generic methods introduced, such as fetchRaw, fetchTuples, fetchDicts,
> or whatever. fetchSql can always have a use, though, so it it not likely
> to become clutter.
I guess you miss something Mario.
Let's take a example .
qualifier=qualifierWithQualifierFormat('lastName=="Hugo"')
fetchSpec=FetchSpecification(entityName='Writer', qualifier=qualifier)
fetchCount(fetchSpec) will return the number of writer with lastname =
'Hugo' ..
and so on this is not so trivial as a simple 'select count(*) ..'
so the answer is no : fetchCount() and fetchSql() haven't the same
purpose, even
is you can use fetSQL to do a fetchCount() or anything else.
And i really like the OO approach of modeling, and i really think that
even fetchSQL() ( which for me should be fetchSQLObject() ) should
alway return a object.
Take a look at this:
- in the model you describe the object mapping (which is not a real object
but only a "result object" .
for example:
CustomSearch
- searchString()
- books relation one-> many
- ...
- I thinks we should find a way to do
ec.objectsWithFetchSpecification('CustomSearch, ..searchString = 'Toto') ..
and so provide a method to do some special SQL in the CustomSearch() which
return CustomSearch object.
But this way you can do something like
search = CustomSearch(...)
books = search.getBooks() ( which return some books object .. not raw SQL)
>Future enhancements
>-------------------
> Interesting future features (NOT included in this proposal) have been
>proposed in this thread, including:
>- fetchSQL(): the possibility to pass a raw sql query to build
> objects,
To build Objects or raw SQL ?
Perhaps we can take a look on how SQLObject do that ?
> - the ability to fetch raw rows (i.e. raw dict) rather than
> fully-initialized objects,
yeah ..
Bye Bye
Bye Bye ..
|