Thread: [SQLObject] trying to override subclass'd SQLObject.get()
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: <jo...@co...> - 2005-12-10 23:43:36
|
I had a problem and I figured a workaround, but I would like someone to t= ry to explain to me what I did wrong originally. Problem: I want the Spam.get() method of my Spam table class to do some e= xtra initialization stuff (row.moreSpam()) after a row is returned (in my real case, I need to pars= e some XML into a DOM object from one of the strings that is returned in the table, but that de= tail is relatively inconsequential to the problem - I built a simple example here which just= adds an extra attribute to the class and the failure is identical). When I started out, I created the following SQLObject subclass: class Spam(SQLObject): eric =3D StringCol(length=3D32) def moreSpam(self): self.idle =3D 'marvelous Spam' # this is where all the 'extra stuff= ' would go Pretty simple. I put in an appropriate connection and did the following: Spam.createTable() Spam(eric=3D'wonderful Spam') row =3D Spam.get(1) row.moreSpam() print row.eric -> displays 'wonderful Spam' print row.idle -> displays 'marvelous Spam' (Of course, the "idle" attribute is not actually connected to the table, = only the "eric" attribute is, but in my case, that's fine. I would add some helper methods to make= sure that if the "idle" attribute is modified, it does the appropriate thing. In my case, it wou= ld reencode the XML and update the table's actual string attribute with the reencoded XML string)= . This works fine, but it is prone to failure since if other uses of get() = occur, then I (or others) might not remember to call the moreSpam() method after the row is returne= d (in my real world case, this is critical since the decoded XML data is necessary for the rest of = the app to work). In any case, the purpose of inheritance/encapsulation is exactly to hide = this kind of stuff so it happens automagically. What I really wanted is the get() method to add t= he extra initialization behavior after it gets back the row object. From past experiences with other languages (Objective-C) using a database= kit similar to SQLObject, I figured I would just override the get() method in my Spam cl= ass, call the "super" get() method from SQLObject (which will get me back the row) and then add= the necessary call to moreSpam(). So I tried adding this to the bottom of the Spam class: def get(cls,id): obj =3D SQLObject.get(cls,id) # get the row object for 'id', your 'n= ormal' get() call obj.moreSpam() # this should add the idle attribute to = the row object return obj # return the row object as get() normall= y would get =3D classmethod(get) # silliness needed to make class metho= ds identifiable Now, if you do: row =3D Spam.get(2) it fails with the following: Traceback (most recent call last): File "<pyshell#48>", line 1, in -toplevel- test.Spam.get(1) File "C:\xampp\xampp\htdocs\cats\test.py", line 23, in get obj =3D SQLObject.get(self,id) File "C:\Python24\lib\site-packages\sqlobject\main.py", line 890, in ge= t id =3D cls.sqlmeta.idType(id) TypeError: int() argument must be a string or a number FRACKLE! I tried different approaches to change the the overridden method and noth= ing improved matters.=20 So, on a hunch, I tried doing a real hack. I removed the stuff I had add= ed to the Spam class and then subclassed the Spam class and put the new get() method there as foll= ows: class SubSpam(Spam): def get(self,id): obj =3D Spam.get(id) obj.doSomething() return obj get =3D classmethod(get) Now, when I did row =3D SubSpam.get(2), everything worked peachy, and the= extra row.idle attribute appeared perfectly with no problem. So this hack works, but I contend it is a hack. I see no reason why I sh= ould have to add another level of class inheritance to this problem. Spam is already a subclass a= nd everything I have done shouldn't cause this problem. The only thing I can see that is different is that I am calling Spam.get(= ) in my sub-subclass whereas originally I was calling SQLObject.get() because that was its sup= erclass. I assume there is some problem with way the metadata connections work in the original de= rived class (Spam) and the SQLObject class when it is called directly). However, it still seems= really fishy to me and I'd love to understand it. Anyone have any ideas? Jon Rosen |
From: <jo...@co...> - 2005-12-10 23:52:27
|
I apologize for the awful formatting of my previous message. My web mail= sender does strange things. Also, I made an error in the description (probably most of you w= ould figure it out ;-)=20 The replacement sub-subclass SubSpam was incorrectly copied (I had been d= oing some silly name changes to adapt to the "monty python" style of communication and forgot = to change these from my actual tests). The properly named version would read: class SubSpam(Spam): def get(cls,id): obj =3D Spam.get(id) obj.moreSpam() return obj get =3D classmethod(get) The problem is still the same. I can't get the proper results when tryin= g to override get() from my main "table-level" subclass in SQLObject, but if I subclass my "table-= level" class and override get() there, with it calling the table-level subclass's get(), things see= m to work okay. Any ideas? Jon |
From: Yuan H. <hon...@gm...> - 2005-12-12 09:20:46
|
Q2xhc3MgbWV0aG9kcyBhcmUgY2FsbGVkIGxpa2UgY2xhc3MubWV0aG9kKHBhcmFtZXRlcnMpLiBU aGUgY2xhc3MKaXRzZWxmIGlzIG5vdCBpbiB0aGUgYXJndW1lbnQgbGlzdC4gVGh1cywgU1FMT2Jq ZWN0LmdldCBleHBlY3RzIGlkIGFzCnRoZSBmaXJzdCBwYXJhbWV0ZXIgaW4geW91ciBwYXJhbWV0 ZXIgbGlzdCwgc2luY2UgdGhlIGNsYXNzIHdpbGwgYmUKYXV0b21hdGljYWxseSBzdXBwbGllZCBi eSB0aGUgcHl0aG9uIGludGVycHJldGVyLgoKWW91ciBjYWxsaW5nIG9mIFNRTE9iamVjdC5nZXQo Y2xzLGlkKSB3aWxsIHN1cHBseSB0aGUgZ2V0IG1ldGhvZCB3aXRoCjMgcGFyYW1ldGVycywgdGhl IGZpcnN0IGJlaW5nIHRoZSBjbGFzcyBTUUxPYmplY3QsIHRoZSBzZWNvbmQgdGhlCmNsYXNzIFNw YW0sIGFuZCB0aGVuIGlkLiBIZW5jZSB0aGUgZXJyb3IuCgpCdXQgSSBkb24ndCBrbm93IGhvdyB0 byBlbGltaW5hdGUgdGhlIGFkZGl0aW9uYWwgbGF5ZXIgb2YgaW5oZXJpdGFuY2UuCgotLQpIb25n IFl1YW4KCrTzudy80s34yc+9qLLEs6zK0ArXsNDe17Dk6r2ossTSu9W+yr25us7vCmh0dHA6Ly93 d3cuaG9tZW1hc3Rlci5jbgoKT24gMTIvMTEvMDUsIGpvbkBjb3JhbDguY29tIDxqb25AY29yYWw4 LmNvbT4gd3JvdGU6Cj4gSSBhcG9sb2dpemUgZm9yIHRoZSBhd2Z1bCBmb3JtYXR0aW5nIG9mIG15 IHByZXZpb3VzIG1lc3NhZ2UuICBNeSB3ZWIgbWFpbCBzZW5kZXIgZG9lcyBzdHJhbmdlCj4gdGhp bmdzLiAgQWxzbywgSSBtYWRlIGFuIGVycm9yIGluIHRoZSBkZXNjcmlwdGlvbiAocHJvYmFibHkg bW9zdCBvZiB5b3Ugd291bGQgZmlndXJlIGl0IG91dCA7LSkKPiBUaGUgcmVwbGFjZW1lbnQgc3Vi LXN1YmNsYXNzIFN1YlNwYW0gd2FzIGluY29ycmVjdGx5IGNvcGllZCAoSSBoYWQgYmVlbiBkb2lu ZyBzb21lIHNpbGx5IG5hbWUKPiBjaGFuZ2VzIHRvIGFkYXB0IHRvIHRoZSAibW9udHkgcHl0aG9u IiBzdHlsZSBvZiBjb21tdW5pY2F0aW9uIGFuZCBmb3Jnb3QgdG8gY2hhbmdlIHRoZXNlIGZyb20g bXkKPiBhY3R1YWwgdGVzdHMpLiAgVGhlIHByb3Blcmx5IG5hbWVkIHZlcnNpb24gd291bGQgcmVh ZDoKPgo+ICBjbGFzcyBTdWJTcGFtKFNwYW0pOgo+ICAgIGRlZiBnZXQoY2xzLGlkKToKPiAgICAg IG9iaiA9IFNwYW0uZ2V0KGlkKQo+ICAgICAgb2JqLm1vcmVTcGFtKCkKPiAgICAgIHJldHVybiBv YmoKPiAgICBnZXQgPSBjbGFzc21ldGhvZChnZXQpCj4KPgo+IFRoZSBwcm9ibGVtIGlzIHN0aWxs IHRoZSBzYW1lLiAgSSBjYW4ndCBnZXQgdGhlIHByb3BlciByZXN1bHRzIHdoZW4gdHJ5aW5nIHRv IG92ZXJyaWRlIGdldCgpIGZyb20KPiBteSBtYWluICJ0YWJsZS1sZXZlbCIgc3ViY2xhc3MgaW4g U1FMT2JqZWN0LCBidXQgaWYgSSBzdWJjbGFzcyBteSAidGFibGUtbGV2ZWwiIGNsYXNzIGFuZCBv dmVycmlkZQo+IGdldCgpIHRoZXJlLCB3aXRoIGl0IGNhbGxpbmcgdGhlIHRhYmxlLWxldmVsIHN1 YmNsYXNzJ3MgZ2V0KCksIHRoaW5ncyBzZWVtIHRvIHdvcmsgb2theS4gIEFueQo+IGlkZWFzPwo+ Cj4gSm9uCj4KPgo+Cj4gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t LS0tLS0tLS0tLS0tLQo+IFRoaXMgU0YubmV0IGVtYWlsIGlzIHNwb25zb3JlZCBieTogU3BsdW5r IEluYy4gRG8geW91IGdyZXAgdGhyb3VnaCBsb2cgZmlsZXMKPiBmb3IgcHJvYmxlbXM/ICBTdG9w ISAgRG93bmxvYWQgdGhlIG5ldyBBSkFYIHNlYXJjaCBlbmdpbmUgdGhhdCBtYWtlcwo+IHNlYXJj aGluZyB5b3VyIGxvZyBmaWxlcyBhcyBlYXN5IGFzIHN1cmZpbmcgdGhlICB3ZWIuICBET1dOTE9B RCBTUExVTkshCj4gaHR0cDovL2Fkcy5vc2RuLmNvbS8/YWRfaWR2MzcmYWxsb2NfaWQWODY1Jm9w Y2xpY2sKPiBfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwo+ IHNxbG9iamVjdC1kaXNjdXNzIG1haWxpbmcgbGlzdAo+IHNxbG9iamVjdC1kaXNjdXNzQGxpc3Rz LnNvdXJjZWZvcmdlLm5ldAo+IGh0dHBzOi8vbGlzdHMuc291cmNlZm9yZ2UubmV0L2xpc3RzL2xp c3RpbmZvL3NxbG9iamVjdC1kaXNjdXNzCj4K |
From: <jo...@co...> - 2005-12-12 12:36:46
|
Ah folks, Problem solved! I think this works only in recent Python (2.3+?) with the "new" object/class types (i.e., those that inherit from "object") although I am not sure of that. I does only work with 2.3+ because it requires the "super" call. Instead of calling SQLObject in my overridden "get" class method, I needed to call super(). Calling SQLObject.get(id) caused the class of SQLObject to be passed to the get() method which meant there was no table metadata that was available to the get() method (hence the error I was getting) since SQLObject is abstract and has no table of its own. So in my Spam.get() method, I now call super(Spam,self).get(id). Super needs the current Class's object first, and then self next but since this is a class method, they are the same thing (I could also have called super(Spam,Spam).get(id) it turns out because in the context of this class method, Spam and cls are the same). Now, this properly calls the superclass's (SQLObject's) get() method, but still passes it the Spam class object so the proper thing happens. The following code: class Spam(SQLObject): eric =3D StringCol(length=3D32) def moreSpam(self): self.idle =3D 'marvelous spam' def get(self,id): obj =3D super(Spam,self).get(id) obj.moreSpam() return obj get =3D classmethod(get) Spam.createTable() Spam(eric=3D'wonderful spam') x =3D Spam.get(1) print 'Eric says: ' + x.eric print 'Idle says: ' + x.idle prints back (as hoped/expected!): > Eric says: marvelous spam > Idle says: wonderful spam Perfecto! Jon |