Re: [Modeling-users] Aggregations with fetchSQL
Status: Abandoned
Brought to you by:
sbigaret
|
From: Sebastien B. <sbi...@us...> - 2003-11-11 17:36:16
|
Hi,
<luk...@po...> wrote:
[...]
SB> Your voice has been heard. However, this won't happen in the coming
SB> days, for sure. Would a integrated fetchSQL() make you happy in the
SB> meantime?
[...]
>=20
> Yes, but I've got the latest version (I think so) 0.9-pre-15 and there is=
n't
> anything like "fetchSQL". Is this "meantime", you've mentioned is present=
or
> a rather near or far future?
> Is there a possibility to run now any query (with aggregations, group by's
> etc.) in Modeling framework? If is, could you suggest how to do it? The
> nicest way would be a simple piece of Python code, but some links to manu=
als
> or another sources of information are also good.
Sorry for not having been any clearer: yes, I meant that this could be
added in the next release. The next release is planned at the end of
the week, probably. Now that I'm back and convalescing I guess it's
time to make things happen again ;)
Back on the subject, yes, there is a possibility to run any query you
want w/ the current release, and previous ones as well. The way it can
be done is exposed in tests' test_EditingContext_Global.py, method
test_999_customSQLQuery(). However, this is unfinished --I've realized
that something more needs to be done. I'm including at the end of the
mail method fetchSQL() which demonstrates how this can be done, and that
you can try as-is with the following examples.
Example of use (using test-model AuthorBooks):
>>> from Modeling.EditingContext import EditingContext
>>> from Modeling import ModelSet
>>> import AuthorBooks, pprint
>>> ec=3DEditingContext()
>>> model=3DModelSet.defaultModelSet().modelNamed('AuthorBooks')
>>> sql=3D"select avg(age), sum(age) from WRITER"
>>> attrs=3D["average_age", "sum_age"]
>>> pprint.pprint(fetchSQL(ec, model, sql, attrs))
[{'average_age': 204.66666666666666, 'sum_age': 614L}]
>>> sql=3D"select avg(age), sum(age), last_name from WRITER GROUP BY(last_n=
ame)"
>>> attrs=3D["average(age)", "sum(age)", "Last name"]
>>> pprint.pprint(fetchSQL(ec, model, sql, attrs))
[{'Last name': 'Cleese', 'average(age)': 24.0, 'sum(age)': 24L},
{'Last name': 'Dard', 'average(age)': 82.0, 'sum(age)': 82L},
{'Last name': 'Rabelais', 'average(age)': 508.0, 'sum(age)': 508L}]
Why do we need a model? To be sure that we get through the right
DBContext down to the database.
Last comment: when aggregating, sometimes you just get aggregates,
sometimes you also get informations on real objects that you might be
interested in retrieving as such (I mean, as real objects). Here is an
example: let's fetch those writers whose books have an average price
less the 4.5:
[ NB: I changed the default test db with:
update book set price=3D2.3 where id=3D1;
update book set price=3D3.3 where id=3D2;
update book set price=3D8.7 where id=3D3;
update book set price=3D1.1 where id=3D4; ]
>>> sql=3D"SELECT t0.id, t0.last_name, AVG(t1.price) FROM WRITER t0 INNER J=
OIN BOOK t1 ON t0.id=3Dt1.fk_writer_id GROUP BY t0.id, t0.last_name HAVING =
AVG(t1.price)<4.5"
>>> attrs=3D["id", "Author's last name", "Average price"]
>>> result=3DfetchSQL(ec, model, sql, attrs)
>>> pprint.pprint(result)
[{"Author's last name": 'Rabelais',
'Average price': 2.2999999999999998,
'id': 2},
{"Author's last name": 'Dard', 'Average price': 4.3666666666666671, 'id': =
3}]
--> Now, if you need the real objects --the corresponding writers--,
simply get them from the raw row (this topic is also discussed here,
from a different point of view however:
http://modeling.sf.net/UserGuide/ec-fetch-raw-rows.html)
>>> authors=3D[ec.faultForRawRow(r, "Writer") for r in result]
>>> pprint.pprint([{a.getLastName():a} for a in authors])
[{'Rabelais': <Writer.Writer instance at 0x84f3114>},
{'Dard': <Writer.Writer instance at 0x8545a6c>}]
In fact, you can turn any dictionary into real objects using
ec.faultForRawRow() at the only condition that the PKs for the entity
is included in the dictionary --that's why WRITER.id was included in
the result set here.
I hope this could fit your requirements. I'll probably add such a method
to EditingContext for the next release, or at least add something liek
this in the documentation. I'll also be pleased to hear from your own
application, at least of the aggregations you're thinking about when
asking this (if you can disclose): it always help to learn about the
context in which feature requests raises.
And to all: if you have any specific requirements, comments etc. about
this, it's probably the time to speak ;)
-- S=E9bastien.
------------------------------------------------------------------------
def fetchSQL(ec, model, sql, attributeNames):
"""A method demonstrating how to fetch using any sql statement within
the MDL framework.
Parameters:
model -- the model related to the db one wants to fetch data from
sql -- a valid sql expression
attributeNames -- the name to give to each column in the result set,
supplied in the order in which they are fetched
Returns: a sequence of dictionaries, mapping attributes'names to the
fetched values
"""
class FakeAttr:
def __init__(self, name):
self._name=3Dname
def name(self):
return self._name
=20=20
attrs=3D[FakeAttr(n) for n in attributeNames]
from Modeling import DatabaseContext
dbContext=3DDatabaseContext.registeredDatabaseContextForModel(model, ec)
adaptorChannel=3DdbContext.availableChannel().adaptorChannel()
sqlExpr=3DadaptorChannel.adaptorContext().adaptor().expressionClass()()
sqlExpr.setStatement(sql)
adaptorChannel.setAttributesToFetch(attrs)
adaptorChannel.adaptorContext().beginTransaction()
res=3D[]
try:
adaptorChannel.evaluateExpression(sqlExpr)
row=3DadaptorChannel.fetchRow()
while row:
res.append(row)
row=3DadaptorChannel.fetchRow()
=20=20=20=20=20=20
adaptorChannel.cancelFetch()
finally:
adaptorChannel.adaptorContext().rollbackTransaction()
return res
------------------------------------------------------------------------
|