Thread: [SQLObject] a third way of accessing attributes
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: Bud P. B. <bu...@si...> - 2003-05-15 13:58:34
Attachments:
dynAttr.py
|
Some time ago, Nick and Ian (and others?) discussed about two ways of exposing SQL data through object attributes: * metaclasses (current SQLObject) that set properties * __getattr__ and __setattr__ There seems to be a third way: * Adding methods and properties (attributes) to a class at runtime (sans metaclasses). The advantage I see (yet to be verified) is that this approach allows a very weak coupling of application objects with the backend implementation: There doesn't even seem to be the need to inherit from some "storable" class or similar (SQLObject). Instead, when the Backend is initialized, it simply loops through all ObjMaps in the Mapping and adds attributes and methods accordingly. I kind of like this... For who is interested in more details, I attach a little test that demonstrates this. --bud /----------------------------------------------------------------- | Bud P. Bruegger, Ph.D. | Sistema (www.sistema.it) | Via U. Bassi, 54 | 58100 Grosseto, Italy | +39-0564-411682 (voice and fax) \----------------------------------------------------------------- |
From: Nick <ni...@dd...> - 2003-05-15 15:20:48
|
On Thu, 2003-05-15 at 07:38, Bud P.Bruegger wrote: > Some time ago, Nick and Ian (and others?) discussed about two ways of > exposing SQL data through object attributes: > > * metaclasses (current SQLObject) that set properties > * __getattr__ and __setattr__ > > There seems to be a third way: > > * Adding methods and properties (attributes) to a class at runtime > (sans metaclasses). The problem with this approach is that you won't be able to automatically resolve foreign keys at access time unless the method for accessing foreign keys is only handled through methods, which both __getattr__ and properties do. Nick |
From: Nick <ni...@dd...> - 2003-05-15 15:24:21
|
On Thu, 2003-05-15 at 10:19, Nick wrote: > On Thu, 2003-05-15 at 07:38, Bud P.Bruegger wrote: > > Some time ago, Nick and Ian (and others?) discussed about two ways of > > exposing SQL data through object attributes: > > > > * metaclasses (current SQLObject) that set properties > > * __getattr__ and __setattr__ > > > > There seems to be a third way: > > > > * Adding methods and properties (attributes) to a class at runtime > > (sans metaclasses). > > The problem with this approach is that you won't be able to > automatically resolve foreign keys at access time unless the method for > accessing foreign keys is only handled through methods, which both > __getattr__ and properties do. Sorry, I forgot to say what my point was :) My point is that it's more convenient to treat objects as persistent using that kind of model than have set() and get() functions a la C (java, etc.). You're doing nearly the exact same thing as properties, so why not use them. Nick |
From: Bud P. B. <bu...@si...> - 2003-05-15 15:47:48
|
On 15 May 2003 10:23:14 -0500 Nick <ni...@dd...> wrote: > On Thu, 2003-05-15 at 10:19, Nick wrote: > > On Thu, 2003-05-15 at 07:38, Bud P.Bruegger wrote: > > > Some time ago, Nick and Ian (and others?) discussed about two ways of > > > exposing SQL data through object attributes: > > > > > > * metaclasses (current SQLObject) that set properties > > > * __getattr__ and __setattr__ > > > > > > There seems to be a third way: > > > > > > * Adding methods and properties (attributes) to a class at runtime > > > (sans metaclasses). > > > > The problem with this approach is that you won't be able to > > automatically resolve foreign keys at access time unless the method for > > accessing foreign keys is only handled through methods, which both > > __getattr__ and properties do. > > Sorry, I forgot to say what my point was :) My point is that it's more > convenient to treat objects as persistent using that kind of model than > have set() and get() functions a la C (java, etc.). You're doing nearly > the exact same thing as properties, so why not use them. Hi Nick, I think there must be a misunderstanding. I _DO_ use properties, just that I set them at runtime (in backend.__init__) instead of from a metaclass. So for all that I'm concerned, I don't see a difference to how the property behaves (No matter at what point of time you added it to the class, it is there...). Maybe some more concrete example helps: Assume I have application classes Person and Role. My Backend instance contains a Mapping instance contains ClassMaps for both Person and Role, as well as a Many2Many Relationship between Person and Role. The Person and Role role classes don't know anything about storage--they are written as if there was no persistence at all. Now I create my Backend instance that runs its __init__. For each ClassMap in its Mapping instance (i.e., ['Person', 'Role']), it does something along the following lines: * add a class attribute called "backendInstance" that is the python object reference of the backend. This is necessary to make sure application objects know who to talk to in order to get their persistence needs satisfied. (this is where I am at the moment) * add methods for controlling persistence. I'm currently thinking of - insert (new in database) - update (update values in database) - synch (update valued from database) - del - as well as functions to control transactions. I was thinking of wrapping the record-oriented methods of the DB-API with object-oriented ones... Gotta think about that one... * add properties to traverse relationships. There seem to be two options here: lazy or automatic. This can be controlled by the Relationship object in the Mapping. I haven't looked into this much and probably I can rip off some of SQLObject's code here.. So in summary, the touch and feel (for application programmer) should be very similar to SQLObject except that the application class does not subclass any persistence related class and that mappings are defined externally to the app class much rather than inside.. later --b /----------------------------------------------------------------- | Bud P. Bruegger, Ph.D. | Sistema (www.sistema.it) | Via U. Bassi, 54 | 58100 Grosseto, Italy | +39-0564-411682 (voice and fax) \----------------------------------------------------------------- |
From: Nick <ni...@dd...> - 2003-05-15 16:02:39
|
On Thu, 2003-05-15 at 10:46, Bud P.Bruegger wrote: > Now I create my Backend instance that runs its __init__. For each > ClassMap in its Mapping instance (i.e., ['Person', 'Role']), it does > something along the following lines: > > * add a class attribute called "backendInstance" that is the python > object reference of the backend. This is necessary to make sure > application objects know who to talk to in order to get their > persistence needs satisfied. (this is where I am at the moment) > > * add methods for controlling persistence. I'm currently thinking of > - insert (new in database) > - update (update values in database) > - synch (update valued from database) > - del > - as well as functions to control transactions. I was thinking of > wrapping the record-oriented methods of the DB-API with > object-oriented ones... Gotta think about that one... > > * add properties to traverse relationships. There seem to be two > options here: lazy or automatic. This can be controlled by the > Relationship object in the Mapping. I haven't looked into this > much and probably I can rip off some of SQLObject's code here.. I do like that model, however you probably don't even need to make properties and such, since it is all internally called and used by other classes and probably never by the API user. At least it would certainly reduce the complexity that metaclasses bring to the game. And by doing that in __init__ you're not really doing much all that different from metaclassing of the Python 1.5 days. If you're storing mappings, just refer to the mappings when you do an operation instead of making yourself a bunch of lambdas. Depending on how many operations you're doing and how many classes you're defining, you may save yourself a lot of up-front work and memory just to import the classes. Nick |
From: Ian B. <ia...@co...> - 2003-05-15 15:58:44
|
On Thu, 2003-05-15 at 07:38, Bud P.Bruegger wrote: > * Adding methods and properties (attributes) to a class at runtime > (sans metaclasses). SQLObject does this in the class method SQLObject.addColumn. The metaclass simply searches _columns and the defined attributes to run addColumn on class creation. The metaclass does other things too, though I've tried to decrease its work as I'm able. Some things, like keeping some class variables from being inherited, still need to be done in the metaclass. The metaclass is really just the equivalent of __init__ for the class -- it handles the class instantiation, where the real __init__ (or __new__/_init) handle instance instantiation. Ian |
From: Luke O. <lu...@me...> - 2003-05-15 16:10:06
|
Quoting Nick <ni...@dd...>: > Sorry, I forgot to say what my point was :) My point is that it's more > convenient to treat objects as persistent using that kind of model than > have set() and get() functions a la C (java, etc.). You're doing nearly > the exact same thing as properties, so why not use them. I think Bud's point was to use properties (see the end of his example), but the important part was to be able to add Backend 'Mappings' to any arbitrary class (not needing classes to be initially tied to SQLObject or similar). I'm not entirely sure I understand the need for this, but it's intriguing. Actually, out of curiousity, I wanted to see if there was any special reason SQLObject addColumn's in the metaclass: class Answer(CoreObject): _columns = [ TextCol('answer'), ## removed ForeignKey('Question') for this test ] class Question(CoreObject): _columns = [ Col('question') ] _joins = [ MultipleJoin('Answer') ] >>> from Answer import Answer >>> from SQLObject import * >>> col = ForeignKey('Question') >>> Answer.addColumn(col) >>> from Question import Question >>> Answer(2).question <Question 1 question='John'> >>> So, SQLObject currently supports dynamically adding properties at runtime (I guess I knew that...). What we gain by pulling this ability into a helper class (like I assume Mapping to be), that allows us to 'addColumn' to any class we please? ArbitraryClass + Mapping == SQLObjectClass + _columns/_joins, yes? Hmm, intriguing, as I said.. - Luke |
From: Bud P. B. <bu...@si...> - 2003-05-15 16:43:58
|
On Thu, 15 May 2003 10:55:32 -0500 Luke Opperman <lu...@me...> wrote: > Hmm, intriguing, as I said.. I was really surprised about this possibility myself. In my personal style, I would like to write application classes independently of any kind of persistence mechanism first and then add persistence in a second step. I guess in SQL object, I'd do that something along the following lines: (and yes, one doesn't need to do it that way in SQLObject) class Person(object): pass #add all business logic here class PersistentPerson(Person, SQLObject) _columns = [...] _joins = _idName = .. _table =... _connection = # I'd personally prefer to deal with that later... So in my current approach, there is a similar structure: class Person(object): pass #add all business logic here persClassMap = ClassMap(....) cheers --b |