[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-28 Thread Michael Bayer


On Jan 27, 2009, at 9:49 AM, Toby Bradshaw wrote:

> Paragraph 1: "Set up name as a synonym to another mapped property."
> Paragraph 4: "name refers to the name of the existing mapped property,
> which can be any other MapperProperty including column-based  
> properties
> and relations."
>
> Paragraph 1 implies that the name passed to synonym is the name of the
> synonym. This is not actually the case. It is the entity to which the
> synonym refers.

the word "name" in paragraph 1 intends to reference the "key" that you  
place in the properties dict.   If you can suggest clearer language  
for this we can patch it.

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-27 Thread Toby Bradshaw

Thanks very much for that detailed explanation which has proved very 
helpful.

In the end it turns out that the particular construct I need is the simple:

mapper(MyClass, table, properties={
 "timeunits": synonym("time_units")
})

coupled with a data-descriptor for 'timeunits' on the object class which 
translates to and from the database-backed attribute.


Careful reading of the docs here: 
http://www.sqlalchemy.org/docs/04/sqlalchemy_orm.html#docstrings_sqlalchemy.orm_modfunc_synonym
 
sheds further light. I'd like to suggest that the first and fourth 
paragraph of that entry infer exactly the opposite meaning.

Paragraph 1: "Set up name as a synonym to another mapped property."
Paragraph 4: "name refers to the name of the existing mapped property, 
which can be any other MapperProperty including column-based properties 
and relations."

Paragraph 1 implies that the name passed to synonym is the name of the 
synonym. This is not actually the case. It is the entity to which the 
synonym refers.

Anyway.. thanks very much for your help on this. Problem solved and I'm 
very grateful for the time and trouble you took, Micheal. Look forward 
to another happy installation of SQLAlchemy here in London.

Regards,

Toby Bradshaw
Senior Networking Engineer,
Ideawork 3d, London, UK.

Michael Bayer wrote:
> On Jan 26, 2009, at 1:29 PM, Toby Bradshaw wrote:
>
>   
>> Michael Bayer wrote:
>> 
>>> also your example should read like this:
>>>
>>> a = session.query(A).all()[0]
>>> print a.time_units
>>> a.time_units = 1
>>> print a.time_units
>>> #print A.timeunits
>>> #print A.time_units
>>>
>>>   
>> Huh ? time_units is the column name in the database. I want to refer  
>> to
>> that column through the attribute 'timeunits'. I also want to use
>> descriptors to do extra work when setting that attribute. The synonym
>> api docs say:
>>
>> *def synonym(/name/, /map_column=False/, /descriptor=None/, / 
>> proxy=False/)*
>> Set up name as a synonym to another mapped property.
>>
>> So when I say:
>>
>> mapper(A, table_A, properties = {
>> "time_units" : synonym("timeunits", map_column = True)
>> })
>>
>>
>> Am I not saying 'create an alias to column 'time_units' in table_A and
>> call it 'timeunits' ??
>> 
>
> I think your confusion is focused on the concept of SQLAlchemy  
> instrumented descriptors, as it seems you're expecting the SQLAlchemy  
> column-mapped descriptor to "wrap" an existing descriptor.   This is  
> not how it works. SQLAlchemy does not populate instance state  
> using getattr()/setattr(), in the default case it populates __dict__  
> directly. The dictionary which it uses can be wrapped with a user- 
> defined "proxying" dictionary but that's not an API you need to get  
> involved with.By moving all low-level operations to __dict__ and  
> all in-python operations to instrumented descriptors, the separation  
> of event-producing attribute access and direct en-masse state access  
> is clear, and the very high-volume activity of populating object state  
> is performed without the overhead of setattr() or event production.
>
> The "synonym" model is provided so that a user-defined descriptor and  
> a SQLAlchemy-column mapped descriptor can co-exist, but instead of  
> being wrapped, they use different names.   So usage of the model  
> means:  one descriptor maps to the column and is entirely generated by  
> SQLAlchemy, the other is your custom descriptor and SQLAlchemy places  
> a proxy around it to provide class-level behavior like Foo.bar ==  
> somevalue (which you also might want to customize, but that's a  
> different issue).   Your custom descriptor is the public face of the  
> attribute, and communicates with the value that exists in the database  
> using the column-mapped descriptor, which is usually treated as  
> private.   Symmetric set/get behavior is provided by the user-defined  
> descriptor as the sole public interface.
>
> So since you'd like to refer to the *translated* attribute as  
> "timeunits", i think you'd want to configure this way:
> mapper(MyClass, table, properties={
>  "timeunits": synonym("time_units")
> })
>
> class MyClass(object):
>  ...
>  timeunits = property(_get, _set)
>
> mapper(MyClass, table, properties={
>  "_timeunits":table.c.time_units,
>  "timeunits": synonym("_timeunits")
> })
>
> which will map the original "time_units" column to the mapped  
> attribute "_timeunits", and the "timeunits" descriptor will provide  
> class-level comparison behavior (i.e. MyClass.timeunits == 5).   the  
> map_column flag does not apply here since you are naming your  
> descriptor something different than the original mapped column.  The  
> column-mapped attribute "_timeunits"  is generally treated as  
> "private" within the application space since it represents directly  
> the "raw" data that is populated/retrieved to/from the database.
>
> you can also leave "time_units" mapped under its own name:
>
> mapper(MyC

[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-26 Thread Michael Bayer


On Jan 26, 2009, at 1:29 PM, Toby Bradshaw wrote:

>
> Michael Bayer wrote:
>> also your example should read like this:
>>
>> a = session.query(A).all()[0]
>> print a.time_units
>> a.time_units = 1
>> print a.time_units
>> #print A.timeunits
>> #print A.time_units
>>
> Huh ? time_units is the column name in the database. I want to refer  
> to
> that column through the attribute 'timeunits'. I also want to use
> descriptors to do extra work when setting that attribute. The synonym
> api docs say:
>
> *def synonym(/name/, /map_column=False/, /descriptor=None/, / 
> proxy=False/)*
> Set up name as a synonym to another mapped property.
>
> So when I say:
>
> mapper(A, table_A, properties = {
> "time_units" : synonym("timeunits", map_column = True)
> })
>
>
> Am I not saying 'create an alias to column 'time_units' in table_A and
> call it 'timeunits' ??

I think your confusion is focused on the concept of SQLAlchemy  
instrumented descriptors, as it seems you're expecting the SQLAlchemy  
column-mapped descriptor to "wrap" an existing descriptor.   This is  
not how it works. SQLAlchemy does not populate instance state  
using getattr()/setattr(), in the default case it populates __dict__  
directly. The dictionary which it uses can be wrapped with a user- 
defined "proxying" dictionary but that's not an API you need to get  
involved with.By moving all low-level operations to __dict__ and  
all in-python operations to instrumented descriptors, the separation  
of event-producing attribute access and direct en-masse state access  
is clear, and the very high-volume activity of populating object state  
is performed without the overhead of setattr() or event production.

The "synonym" model is provided so that a user-defined descriptor and  
a SQLAlchemy-column mapped descriptor can co-exist, but instead of  
being wrapped, they use different names.   So usage of the model  
means:  one descriptor maps to the column and is entirely generated by  
SQLAlchemy, the other is your custom descriptor and SQLAlchemy places  
a proxy around it to provide class-level behavior like Foo.bar ==  
somevalue (which you also might want to customize, but that's a  
different issue).   Your custom descriptor is the public face of the  
attribute, and communicates with the value that exists in the database  
using the column-mapped descriptor, which is usually treated as  
private.   Symmetric set/get behavior is provided by the user-defined  
descriptor as the sole public interface.

So since you'd like to refer to the *translated* attribute as  
"timeunits", i think you'd want to configure this way:

class MyClass(object):
 ...
 timeunits = property(_get, _set)

mapper(MyClass, table, properties={
 "_timeunits":table.c.time_units,
 "timeunits": synonym("_timeunits")
})

which will map the original "time_units" column to the mapped  
attribute "_timeunits", and the "timeunits" descriptor will provide  
class-level comparison behavior (i.e. MyClass.timeunits == 5).   the  
map_column flag does not apply here since you are naming your  
descriptor something different than the original mapped column.  The  
column-mapped attribute "_timeunits"  is generally treated as  
"private" within the application space since it represents directly  
the "raw" data that is populated/retrieved to/from the database.

you can also leave "time_units" mapped under its own name:

mapper(MyClass, table, properties={
 "timeunits": synonym("time_units")
})

in which case your descriptor would communicate raw values to/from the  
"time_units" attribute, which is mapped automatically to its own name.

Also consider that direct translation of a single column can be  
accomplished at the Table level using a custom TypeDecorator.   If you  
went with that route, there would be no need to use synonym() or  
custom descriptors.




--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-26 Thread Toby Bradshaw

Michael Bayer wrote:
> also your example should read like this:
>
> a = session.query(A).all()[0]
> print a.time_units
> a.time_units = 1
> print a.time_units
> #print A.timeunits
> #print A.time_units
>
Huh ? time_units is the column name in the database. I want to refer to 
that column through the attribute 'timeunits'. I also want to use 
descriptors to do extra work when setting that attribute. The synonym 
api docs say:

*def synonym(/name/, /map_column=False/, /descriptor=None/, /proxy=False/)*
Set up name as a synonym to another mapped property.

So when I say:

mapper(A, table_A, properties = {
 "time_units" : synonym("timeunits", map_column = True)
})


Am I not saying 'create an alias to column 'time_units' in table_A and 
call it 'timeunits' ??


Incidentally.. creating this mapping replaces the existing property/data 
descriptor class variable with an instance of a Mapper:

print A.timeunits
mapper(A, table_A, properties = {
  "time_units" : synonym("timeunits", map_column = True)
})
print A.timeunits

 >> ./test.py

.timeunits

Unless Mapper takes special care of any existing descriptors how can 
they co-exist with mapped attributes ?

--
t o b e

> the A.time_units is the class-bound descriptor so that raises an 
> exception due to a missing __str__() method.  this is a small bug but 
> does not break the functionality you're looking for.   The in-python 
> access to the attribute is performed via the attribute you've created, 
> i.e. a.time_units.
>
>
> On Jan 26, 2009, at 12:59 PM, Toby Bradshaw wrote:
>
>> CREATE TABLE example_a
>> (
>>  id integer NOT NULL,
>>  time_units integer,
>>  CONSTRAINT example_a_pkey PRIMARY KEY (id)
>> )
>>
>> and (based on 
>> http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors) 
>> :
>>
>> from sqlalchemy import *
>> from sqlalchemy.orm import sessionmaker, mapper, synonym
>>
>> class A(object):
>>  def _set_t(self, t):
>>print "SET"
>>self.timeunits = t / float(10)
>>  def _get_t(self):
>>print "GET"
>>return self.timeunits * 10
>>  time_units = property(_get_t, _set_t)
>>
>> engine = create_engine("postgres://tobe:@localhost/test")
>> engine.echo = False
>>
>> session_class = sessionmaker(
>>  bind = engine, autoflush = True, autocommit = False,
>> )
>>
>> meta = MetaData(bind = engine)
>> table_A = Table("example_a", meta, autoload = True)
>>
>> print A.time_units
>> mapper(A, table_A, properties = {
>>  "time_units" : synonym("timeunits", map_column = True)
>> })
>>
>> session = session_class()
>>
>> a = session.query(A).all()[0]
>> print a.timeunits
>> a.timeunits = 1
>> print a.timeunits
>> print A.timeunits
>> print A.time_units
>
>
> >

-- 
t o b e
--
A truly clever developer will create code so easy to understand that a less 
than average developer could debug it.


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-26 Thread Michael Bayer
also your example should read like this:

a = session.query(A).all()[0]
print a.time_units
a.time_units = 1
print a.time_units
#print A.timeunits
#print A.time_units

the A.time_units is the class-bound descriptor so that raises an  
exception due to a missing __str__() method.  this is a small bug but  
does not break the functionality you're looking for.   The in-python  
access to the attribute is performed via the attribute you've created,  
i.e. a.time_units.


On Jan 26, 2009, at 12:59 PM, Toby Bradshaw wrote:

> CREATE TABLE example_a
> (
>  id integer NOT NULL,
>  time_units integer,
>  CONSTRAINT example_a_pkey PRIMARY KEY (id)
> )
>
> and (based on
> http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors):
>
> from sqlalchemy import *
> from sqlalchemy.orm import sessionmaker, mapper, synonym
>
> class A(object):
>  def _set_t(self, t):
>print "SET"
>self.timeunits = t / float(10)
>  def _get_t(self):
>print "GET"
>return self.timeunits * 10
>  time_units = property(_get_t, _set_t)
>
> engine = create_engine("postgres://tobe:@localhost/test")
> engine.echo = False
>
> session_class = sessionmaker(
>  bind = engine, autoflush = True, autocommit = False,
> )
>
> meta = MetaData(bind = engine)
> table_A = Table("example_a", meta, autoload = True)
>
> print A.time_units
> mapper(A, table_A, properties = {
>  "time_units" : synonym("timeunits", map_column = True)
> })
>
> session = session_class()
>
> a = session.query(A).all()[0]
> print a.timeunits
> a.timeunits = 1
> print a.timeunits
> print A.timeunits
> print A.time_units


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-26 Thread Michael Bayer

you're running 0.5.0rc2, an early release candidate of 0.5.  There  
have been five bugfix releases since then, have you tried running on  
the latest release ?


On Jan 26, 2009, at 12:59 PM, Toby Bradshaw wrote:

>
> Ok.. given:
>
> CREATE TABLE example_a
> (
>  id integer NOT NULL,
>  time_units integer,
>  CONSTRAINT example_a_pkey PRIMARY KEY (id)
> )
>
> and (based on
> http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors):
>
> from sqlalchemy import *
> from sqlalchemy.orm import sessionmaker, mapper, synonym
>
> class A(object):
>  def _set_t(self, t):
>print "SET"
>self.timeunits = t / float(10)
>  def _get_t(self):
>print "GET"
>return self.timeunits * 10
>  time_units = property(_get_t, _set_t)
>
> engine = create_engine("postgres://tobe:@localhost/test")
> engine.echo = False
>
> session_class = sessionmaker(
>  bind = engine, autoflush = True, autocommit = False,
> )
>
> meta = MetaData(bind = engine)
> table_A = Table("example_a", meta, autoload = True)
>
> print A.time_units
> mapper(A, table_A, properties = {
>  "time_units" : synonym("timeunits", map_column = True)
> })
>
> session = session_class()
>
> a = session.query(A).all()[0]
> print a.timeunits
> a.timeunits = 1
> print a.timeunits
> print A.timeunits
> print A.time_units
> ~
>
> ~
>
>
>
> Running the above:
>
> t...@bismuth:~/src/tobe/bdp_webif$ python test.py
> 
> 10
> 1
> .timeunits
> Traceback (most recent call last):
>  File "test.py", line 36, in 
>print A.time_units
>  File
> "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.0rc2-py2.5.egg/ 
> sqlalchemy/orm/attributes.py",
> line 121, in __str__
>return repr(self.parententity) + "." + self.property.key
>  File
> "/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.0rc2-py2.5.egg/ 
> sqlalchemy/orm/attributes.py",
> line 184, in __getattr__
>return getattr(descriptor, attribute)
> AttributeError: 'property' object has no attribute 'parententity'
>
>
> So you can clearly see that the get and set are not called when
> accessing timeunits. They *are* called when accessing time_units (not
> shown). If you substitute '_email' for 'timeunits' and 'email' for
> 'time_units' then I believe this is an identical pattern to that shown
> in the documentation on
> this:http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors.
> The same behaviour is observed if I try an example with manually
> specified columns.
>
> So.. again.. am I simply not getting something here, is the example
> misleading or wrong or is the code broke in some way.
>
> Thanks in advance,
>
> --
> Toby Bradshaw
> Ideaworks 3d Ltd,
> London, UK.
>
> Michael Bayer wrote:
>> On Jan 23, 2009, at 1:43 PM, Toby Bradshaw wrote:
>>
>> the synonym() construct and the mapped attribute it creates  
>> represents
>> a proxy to the mapped column only in the context of accessing and
>> setting values within the python application space.  It is not  
>> invoked
>> when the database populates the value of the mapped column itself.
>> the general idea of synonym is that the column-based information  
>> stays
>> represented on the mapped instance in the identical manner that it
>> does within the database, and the synonym-based attribute interprets
>> application-level values into the column representation.
>>
>> it should definitely be invoked when setting the property of an
>> existing instance of B, so if that's not working you can share with  
>> us
>> exactly how you are configuring things, since you might be missing
>> some detail.
>>
>>>
>>
>
> >


--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-26 Thread Toby Bradshaw

Ok.. given:

CREATE TABLE example_a
(
  id integer NOT NULL,
  time_units integer,
  CONSTRAINT example_a_pkey PRIMARY KEY (id)
)

and (based on 
http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors):

from sqlalchemy import *
from sqlalchemy.orm import sessionmaker, mapper, synonym

class A(object):
  def _set_t(self, t):
print "SET"
self.timeunits = t / float(10)
  def _get_t(self):
print "GET"
return self.timeunits * 10
  time_units = property(_get_t, _set_t)

engine = create_engine("postgres://tobe:@localhost/test")
engine.echo = False

session_class = sessionmaker(
  bind = engine, autoflush = True, autocommit = False,
)

meta = MetaData(bind = engine)
table_A = Table("example_a", meta, autoload = True)

print A.time_units
mapper(A, table_A, properties = {
  "time_units" : synonym("timeunits", map_column = True)
})

session = session_class()

a = session.query(A).all()[0]
print a.timeunits
a.timeunits = 1
print a.timeunits
print A.timeunits
print A.time_units
~   

~  



Running the above:

t...@bismuth:~/src/tobe/bdp_webif$ python test.py

10
1
.timeunits
Traceback (most recent call last):
  File "test.py", line 36, in 
print A.time_units
  File 
"/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.0rc2-py2.5.egg/sqlalchemy/orm/attributes.py",
 
line 121, in __str__
return repr(self.parententity) + "." + self.property.key
  File 
"/usr/lib/python2.5/site-packages/SQLAlchemy-0.5.0rc2-py2.5.egg/sqlalchemy/orm/attributes.py",
 
line 184, in __getattr__
return getattr(descriptor, attribute)
AttributeError: 'property' object has no attribute 'parententity'


So you can clearly see that the get and set are not called when 
accessing timeunits. They *are* called when accessing time_units (not 
shown). If you substitute '_email' for 'timeunits' and 'email' for 
'time_units' then I believe this is an identical pattern to that shown 
in the documentation on 
this:http://www.sqlalchemy.org/docs/05/mappers.html#using-descriptors.
The same behaviour is observed if I try an example with manually 
specified columns.

So.. again.. am I simply not getting something here, is the example 
misleading or wrong or is the code broke in some way.

Thanks in advance,

--
Toby Bradshaw
Ideaworks 3d Ltd,
London, UK.

Michael Bayer wrote:
> On Jan 23, 2009, at 1:43 PM, Toby Bradshaw wrote:
>
> the synonym() construct and the mapped attribute it creates represents  
> a proxy to the mapped column only in the context of accessing and  
> setting values within the python application space.  It is not invoked  
> when the database populates the value of the mapped column itself. 
> the general idea of synonym is that the column-based information stays  
> represented on the mapped instance in the identical manner that it  
> does within the database, and the synonym-based attribute interprets  
> application-level values into the column representation.
>
> it should definitely be invoked when setting the property of an  
> existing instance of B, so if that's not working you can share with us  
> exactly how you are configuring things, since you might be missing  
> some detail.
>
> >
>   

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: Mixing synonyms and properties with reflected tables..

2009-01-23 Thread Michael Bayer


On Jan 23, 2009, at 1:43 PM, Toby Bradshaw wrote:

> But what happens (if you add a few prints and whatnot to illustrate)  
> is
> that the property setter is never called either when objects are being
> instanced from the database or when setting the property of an  
> existing
> instance of B. Seems that the reflection mechanism has hijacked (via
> it's own descriptors I'm imagining) attribute access in a way that  
> makes
> it impossible for this kind of symmetrical translation to occur.
>
> I would have thought this might be a relatively common problem with
> legacy databases so I'd thought check with the list to see if a  
> solution
> or workaround had already been found. Couldn't find an obvious  
> parallel
> in the list archives so I'm appealing to the group memory for some  
> help
> on this.
>
> Am I just getting this wrong or have I just come against something  
> that
> just can't currently be done ? I can thing of alternative schemes that
> might work but they seem to lose me the ability to use the synonym as
> spelled in filter expressions



the synonym() construct and the mapped attribute it creates represents  
a proxy to the mapped column only in the context of accessing and  
setting values within the python application space.  It is not invoked  
when the database populates the value of the mapped column itself. 
the general idea of synonym is that the column-based information stays  
represented on the mapped instance in the identical manner that it  
does within the database, and the synonym-based attribute interprets  
application-level values into the column representation.

it should definitely be invoked when setting the property of an  
existing instance of B, so if that's not working you can share with us  
exactly how you are configuring things, since you might be missing  
some detail.

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~--~~~~--~~--~--~---