Using __setattr__ in sqlalchemy classes

521 views
Skip to first unread message

Bill Campbell

unread,
Mar 10, 2008, 8:17:58 PM3/10/08
to sqlal...@googlegroups.com
I am just getting up to speed with sqlalchemy, and spent a good
part of last weekend reading the on-line documentation, as well
as looking into the sources.

I would like to use __setattr__ in a sqlalchemy class to handle
certain fields, but have not been able to figure out the
appropriate syntax. Normally I would have something like:

class MyClass(MyOtherClass):
def __setattr__(self, key, val):
if key == 'field1':
# do something
elif key == 'field2':
# do something else
else: # default
MyOtherClass.__setattr__(self, key, val)

How would one do this using sqlalchemy with something like:

from sqlalchemy import *
from sqlalchemy import *

myclass_table = Table(...)

Class MyClass(object):
# stuff here

mapper(MyClass, myclass_table, ...)

Bill
--
INTERNET: bi...@celestial.com Bill Campbell; Celestial Software LLC
URL: http://www.celestial.com/ PO Box 820; 6641 E. Mercer Way
FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676

Cutting the space budget really restores my faith in humanity. It
eliminates dreams, goals, and ideals and lets us get straight to the
business of hate, debauchery, and self-annihilation. -- Johnny Hart

Michael Bayer

unread,
Mar 11, 2008, 10:20:03 AM3/11/08
to sqlal...@googlegroups.com

On Mar 10, 2008, at 8:17 PM, Bill Campbell wrote:

>
> I am just getting up to speed with sqlalchemy, and spent a good
> part of last weekend reading the on-line documentation, as well
> as looking into the sources.
>
> I would like to use __setattr__ in a sqlalchemy class to handle
> certain fields, but have not been able to figure out the
> appropriate syntax. Normally I would have something like:
>
> class MyClass(MyOtherClass):
> def __setattr__(self, key, val):
> if key == 'field1':
> # do something
> elif key == 'field2':
> # do something else
> else: # default
> MyOtherClass.__setattr__(self, key, val)
>
> How would one do this using sqlalchemy with something like:
>
> from sqlalchemy import *
> from sqlalchemy import *
>
> myclass_table = Table(...)
>
> Class MyClass(object):
> # stuff here
>
> mapper(MyClass, myclass_table, ...)
>

implementing your own __setattr__ by itself is not an issue, as long
as you ultimately call the __setattr__ present on "object" to set the
attribute value (assuming you want the value to be set). In other
words, you can't populate __dict__ directly since SQLAlchemy relies on
Python descriptors to issue events and track changes. The example
you have above would be fine (assuming you don't want "field1" and
"field2" to actually be assigned).

If you're just looking for very basic attribute setter/getter
behavior, we also encourage the usage of user-defined Python
descriptors which can be integrated into a mapping using the synonym()
function. In your case it depends upon what you are specifically
looking to accomplish that would help make the choice here.

For more dramatic class re-engineering, there is an option to
completely redefine how SQLAlchemy instruments classes which is being
worked on in a development branch right now, but so far this sounds
like more than you need.

Bill Campbell

unread,
Mar 11, 2008, 12:06:06 PM3/11/08
to sqlal...@googlegroups.com

Setting those fields is in the ``# stuff here'' :-).

>If you're just looking for very basic attribute setter/getter
>behavior, we also encourage the usage of user-defined Python
>descriptors which can be integrated into a mapping using the synonym()
>function. In your case it depends upon what you are specifically
>looking to accomplish that would help make the choice here.

My purpose is to be able to call general setattr from things like
screen processing routines where I have an arbitrary fieldname
and value to update, and to be able to handle cases where other
tables may need updating (e.g. changing a quantity in an order
detail line requires updating extensions, updating quantity
committed in an inventory record, etc.). It would also handle
simple edits of input values, converting to upper or lower case,
reformatting phone numbers, and similar edits.

I didn't understand the possibilities of the synonum mapping on
my first reading of the mapper documentation (and am not sure
that I understand it yet without playing with it). In the
documentation, the ``email'' attribute is the one in the database
table, and ``_email'' is a local attribute in the class?

>For more dramatic class re-engineering, there is an option to
>completely redefine how SQLAlchemy instruments classes which is being
>worked on in a development branch right now, but so far this sounds
>like more than you need.

I have a lot of learning to do before I can get into that.

Bill
--
INTERNET: bi...@celestial.com Bill Campbell; Celestial Software LLC
URL: http://www.celestial.com/ PO Box 820; 6641 E. Mercer Way
FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676

Never blame a legislative body for not doing something. When they do
nothing, that don't hurt anybody. When they do something is when they
become dangerous. -- Will Rogers

svilen

unread,
Mar 11, 2008, 12:31:10 PM3/11/08
to sqlal...@googlegroups.com
> My purpose is to be able to call general setattr from things like
> screen processing routines where I have an arbitrary fieldname
> and value to update, and to be able to handle cases where other
> tables may need updating (e.g. changing a quantity in an order
> detail line requires updating extensions, updating quantity
> committed in an inventory record, etc.).
if i may jump here:
see http://dev.gafol.net/t/aggregator or
http://dbcook.svn.sourceforge.net/viewvc/dbcook/dbcook/misc/aggregator
for handling of such db-related dependencies. see the
tests/convertertest.py for some like-real usage e.g. keeping
accounting balances from transactions.

Bill Campbell

unread,
Mar 11, 2008, 12:57:15 PM3/11/08
to sqlal...@googlegroups.com
On Tue, Mar 11, 2008, svilen wrote:
>
>> My purpose is to be able to call general setattr from things like
>> screen processing routines where I have an arbitrary fieldname
>> and value to update, and to be able to handle cases where other
>> tables may need updating (e.g. changing a quantity in an order
>> detail line requires updating extensions, updating quantity
>> committed in an inventory record, etc.).
>if i may jump here:
>see http://dev.gafol.net/t/aggregator or
>http://dbcook.svn.sourceforge.net/viewvc/dbcook/dbcook/misc/aggregator
>for handling of such db-related dependencies. see the
>tests/convertertest.py for some like-real usage e.g. keeping
>accounting balances from transactions.

Thanks for the pointer. That looks interesting.

BTW: The svn link above appears to be obsolete. There's a new
one on the gafol.net site.

Bill
--
INTERNET: bi...@celestial.com Bill Campbell; Celestial Software LLC
URL: http://www.celestial.com/ PO Box 820; 6641 E. Mercer Way
FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676

My reading of history convinces me that most bad government results
from too much government. --Thomas Jefferson.

svilen

unread,
Mar 11, 2008, 1:11:13 PM3/11/08
to sqlal...@googlegroups.com
On Tuesday 11 March 2008 18:57:15 Bill Campbell wrote:
> On Tue, Mar 11, 2008, svilen wrote:
> >> My purpose is to be able to call general setattr from things
> >> like screen processing routines where I have an arbitrary
> >> fieldname and value to update, and to be able to handle cases
> >> where other tables may need updating (e.g. changing a quantity
> >> in an order detail line requires updating extensions, updating
> >> quantity committed in an inventory record, etc.).
> >
> >if i may jump here:
> >see http://dev.gafol.net/t/aggregator or
> >http://dbcook.svn.sourceforge.net/viewvc/dbcook/dbcook/misc/aggreg
> >ator for handling of such db-related dependencies. see the

> >tests/convertertest.py for some like-real usage e.g. keeping
> >accounting balances from transactions.
>
> Thanks for the pointer. That looks interesting.
>
> BTW: The svn link above appears to be obsolete. There's a new
> one on the gafol.net site.
they were same thing at some time but we have not merged; gafol has
some tests fixed, dbcook's has SA0.3 support... YMMV. i have the
dbcook's one in deployed in real product.
svn co
https://dbcook.svn.sourceforge.net/svnroot/dbcook/trunk/dbcook/misc/aggregator

Michael Bayer

unread,
Mar 11, 2008, 1:36:03 PM3/11/08
to sqlal...@googlegroups.com

On Mar 11, 2008, at 12:06 PM, Bill Campbell wrote:

>
> My purpose is to be able to call general setattr from things like
> screen processing routines where I have an arbitrary fieldname
> and value to update, and to be able to handle cases where other
> tables may need updating (e.g. changing a quantity in an order
> detail line requires updating extensions, updating quantity
> committed in an inventory record, etc.). It would also handle
> simple edits of input values, converting to upper or lower case,
> reformatting phone numbers, and similar edits.

if you're looking to set attributes generically from the outside, you
could just use the regular python setattr function - setattr(object,
key, value).

the case where you need something to happen in response to a setattr
you could do the way you proposed, using __setattr__(), or by using
descriptors. We usually point to using descriptors since they are
defined on a per-attribute basis, rather than needing to build a
complicated if/else block inside of __setattr__.

>
>
> I didn't understand the possibilities of the synonum mapping on
> my first reading of the mapper documentation (and am not sure
> that I understand it yet without playing with it). In the
> documentation, the ``email'' attribute is the one in the database
> table, and ``_email'' is a local attribute in the class?

in the example, the database column is called "email", sqlalchemy maps
the "_email" attribute to that column, then the user-facing "email"
descriptor does what it needs to and then populates the "underlying"
value using the "_email" attribute. The only reason the synonym()
function is used at all is so that the "email" attribute is available
for querying - i.e. query.filter(MyAddress.email ==
'f...@bar.com')...it doesn't affect the "email" attribute beyond that.

>> For more dramatic class re-engineering, there is an option to
>> completely redefine how SQLAlchemy instruments classes which is being
>> worked on in a development branch right now, but so far this sounds
>> like more than you need.
>
> I have a lot of learning to do before I can get into that.

doesn't sound like its needed here.

Bill Campbell

unread,
Mar 11, 2008, 2:01:03 PM3/11/08
to sqlal...@googlegroups.com
On Tue, Mar 11, 2008, Michael Bayer wrote:
>
>
>On Mar 11, 2008, at 12:06 PM, Bill Campbell wrote:
>
>>
>> My purpose is to be able to call general setattr from things like
>> screen processing routines where I have an arbitrary fieldname
>> and value to update, and to be able to handle cases where other
>> tables may need updating (e.g. changing a quantity in an order
>> detail line requires updating extensions, updating quantity
>> committed in an inventory record, etc.). It would also handle
>> simple edits of input values, converting to upper or lower case,
>> reformatting phone numbers, and similar edits.
>
>if you're looking to set attributes generically from the outside, you
>could just use the regular python setattr function - setattr(object,
>key, value).

My intention is to use the normal setattr calls, which I assume call the
underlying setattr functions if they are defined.

>the case where you need something to happen in response to a setattr
>you could do the way you proposed, using __setattr__(), or by using
>descriptors. We usually point to using descriptors since they are
>defined on a per-attribute basis, rather than needing to build a
>complicated if/else block inside of __setattr__.

I may be a bit thick here, but I don't understand descriptors, and don't
find anything in the document other than engine_descriptions or database
specific descriptors.

In practice, I probably wouldn't use complex if/else (although I have some
monster switch statements in some C programs I that do similar things with
database updates :-). I should probably be using stored procedures and
triggers for this (postgres back end), but at this point I'm trying to get
my head around sqlalchemy.

Bill
--
INTERNET: bi...@celestial.com Bill Campbell; Celestial Software LLC
URL: http://www.celestial.com/ PO Box 820; 6641 E. Mercer Way
FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676

...I'm not one of those who think Bill Gates is the devil. I simply
suspect that if Microsoft ever met up with the devil, it wouldn't need an
interpreter. -- Nick Petreley

Michael Bayer

unread,
Mar 11, 2008, 2:57:26 PM3/11/08
to sqlal...@googlegroups.com

On Mar 11, 2008, at 2:01 PM, Bill Campbell wrote:

>
> I may be a bit thick here, but I don't understand descriptors, and
> don't
> find anything in the document other than engine_descriptions or
> database
> specific descriptors.

descriptors are a Python thing, which are used to turn an ordinary
instance attribute into a getter/setter function:

http://docs.python.org/ref/descriptors.html

and usually, descriptors are made more easily using the "property()"
function, which is described with a little example at : http://docs.python.org/lib/built-in-funcs.html

PJE makes the case for the "property" builtin in his well-known post
"Python is not Java" : http://dirtsimple.org/2004/12/python-is-not-java.html

- mike

Bill Campbell

unread,
Mar 11, 2008, 4:56:40 PM3/11/08
to sqlal...@googlegroups.com
On Tue, Mar 11, 2008, Michael Bayer wrote:

Many thanks Mike.

I've only been programming python for about four years now, and I
have not gotten deep into what I consider the fancier stuff (nor,
in the twenty or so years I did most of my work in perl did I
ever get into the areas that people like Damian Conway do :-).

Bill
--
INTERNET: bi...@celestial.com Bill Campbell; Celestial Software LLC
URL: http://www.celestial.com/ PO Box 820; 6641 E. Mercer Way
FAX: (206) 232-9186 Mercer Island, WA 98040-0820; (206) 236-1676

If the personal freedoms guaranteed by the Constitution inhibit the
government's ability to govern the people, we should look to limit those
guarantees. -- President Bill Clinton, August 12, 1993

Reply all
Reply to author
Forward
0 new messages