Thread: RE: [SQLObject] proxy attributes
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: <jws...@ra...> - 2004-02-17 17:37:44
|
>> Note that from the standpoint of the cust_account object, these are all >> one-to-one relations. >if the relations are strict one-to-one, then why data is divided into >separate tables? They are actually many-to-one, but from this side of the join it looks like a one-to-one. This customer account has that one buyer. From the buyer side of the join, that one buyer has many potential customer accounts. >perhaps relationships may be optional, e.g. some customers are buyers, >and others are not? if this is the case, then you may consider using >SQLObject inheritance patch by Daniel Savard. No, customers = accounts and buyers = people. Inheritance is one way of looking at it, though. I will investigate that. |
From: <jws...@ra...> - 2004-02-17 17:38:52
|
>least, if you never want a distinct object representation of one table >vs. the other. Though in that case, the utility of the separate tables >is questionable -- is it really one-to-zero-or-one? Well, then the I don't really care about the extra tables. They will initalized once at install-time and from then on the app will be handling only the top level objects. The app will think the email address is a property of the customer account, but it is really stored as a attribute of the buyer, which is a foreign key from the customer account. I want to present an simple object interface on one side and a correct relational storage on the other side. Creating that interface is rather tedious. The ability of SQLObject to reverse engineer the schema is rather compelling and would factor out huge amounts of finger-grinding code. The value proposition is to bury all the complexity inside the SO classes. If I compromise the relational schema to impedance-match the application, I'll paint myself into a functionality corner eventually. >And since you are using Postgres, views are another possibility. I >don't much care for them, but they seem like a good fit to what you're >doing. Possibly, but this could fit into SO, and then could be usable on various back-ends. Are writeable views available on all modern versions of Postgres? |
From: <jws...@ra...> - 2004-02-17 18:25:07
|
>I think you may have an issue here. What benefits are you getting from >SQLObject if you don't model those 'private' tables? I have similar database >setups (presenting multiple tables as unified objects), and model the entire >implementation. When you say you don't want to create objects for all tables, >I'm thinking you don't want your implementation-specific objects to be part of >your public interface, but I don't think you're going to get the ease of >implementing these proxies in SQLObject without having private objects for >every relevant table. I should clarify- I, as the app do not care about the lesser tables. I, as the programmer want to do as little typing as possible to accomplish the task. The schema is the map and all the necessary information is in there to build the classes automatically. When I define my classes, I would like to be able to say Here are my object names, here are my root tables, go build it. If I could get by with only defining my public(app-facing)classes and letting SO build whatever private(internal) classes it needs, I would be ecstatic, but that's likely hoping for too much. In my specific case, there is a lot of structure in the database schema that I would rather not repeat by manual transcription into my classes. The scope of SO also includes less-featureful backends and the requirement of handling legacy data stores in various degrees of normalization. If it can be derived automatically, that saves me the burden of code synchronization and reduces the chance of errors. If no exploitable schema exists, we can fall back to asking for specifications. |
From: Ian B. <ia...@co...> - 2004-02-17 18:40:20
|
jws...@ra... wrote: > I should clarify- I, as the app do not care about the lesser tables. > I, as the programmer want to do as little typing as possible to > accomplish the task. The schema is the map and all the necessary > information is in there to build the classes automatically. When I > define my classes, I would like to be able to say Here are my object > names, here are my root tables, go build it. If I could get by with > only defining my public(app-facing)classes and letting SO build > whatever private(internal) classes it needs, I would be ecstatic, but > that's likely hoping for too much. Well, from what you're saying, all you really want is something that finds all the tables and creates classes from scratch. Like _fromDatabase, only actually creating all the classes. (I also think _fromDatabase isn't so good at foreign key identification, but that's fixable, at least for Postgres) That seems much more doable. You'd still have to do various traversals. Like, if you have a one-to-many relationship between A and B, and B has an attribute "email", then you have to do a.b.email, instead of simply a.email -- that's a separate feature entirely, and seems particularly difficult to solve while also doing automatic class generation. (Probably not impossible, maybe not even difficult, but not where you want to start) Anyway, Maxwell Hammer had a note about automatic class generation a couple days ago. Expanding tableExists to find all tables is easy enough. You'd probably want to run this automatic class generation after you instantiate your Smart Classes (i.e., ones that have actual business logic), and then that would fill in all the missing tables. Oh, and you have to fix _fromDatabase so that it detects foreign keys, and can find classes based on table name. But altogether a fairly doable plan. Ian |
From: <jws...@ra...> - 2004-02-17 19:00:29
|
>That seems much more doable. You'd still have to do various traversals. > Like, if you have a one-to-many relationship between A and B, and B >has an attribute "email", then you have to do a.b.email, instead of >simply a.email -- that's a separate feature entirely, and seems My immediate itch is many-to-one. I had not thought much about the one-to-many case. I don't see any reason the existing scheme of returning a list of objects would not work for me. It's hard to for me to evaluate my specific issue AND try to consider the One Correct Way. I also labor under the disadvantage of near-complete ignorance of the magic of metaclass programming. Enthusiasm and ignorance - A winning combination! >Oh, and you have to fix _fromDatabase so that it detects foreign keys, >and can find classes based on table name. But altogether a fairly >doable plan. This is beginning to sound like traversal in Zope. |
From: <jws...@ra...> - 2004-02-17 19:31:14
|
>> No, customers = accounts and buyers = people. Inheritance is one way of >> looking at it, though. I will investigate that. > >perhaps not. (i do not think that you mean "account is a man" >inheritance, do you?) in our application, we had an object-oriented Actually, I do. Since python has multiple inheritance, you can see a parallel between joins and inheritance. Think of it like a mixin class. Not "account IS a man", account has the properties of a man. Since we are talking about databases, these are attribute-only classes. No methods are inherited, since none exist. In the two parallel universes- The account table joins to the buyers table, and the buyers attributes are accquired by the account. An account object inherits from the buyer class and merges the buyer attributes into itself. I will investigate the inheritance code. Perhaps that would more appropriate if there is interest in that approach. |
From: alexander s. <al...@an...> - 2004-02-17 20:20:17
|
jws...@ra... wrote, at 17.02.2004 21:26: >>(i do not think that you mean "account is a man" inheritance, do you?) > > Actually, I do. oh, please don't! using inheritance ("is-a" relationship) instead of client ("has-a") relationship between classes is highly error-prone and often difficult to refactor later. probably the best paper i've seen on the matter is chapter "Using inheritance well" of the "Object-Oriented Software Construction" book by Bertrand Meyer. this chapter may be downloaded from http://archive.eiffel.com/doc/manuals/technology/oosc/acrobat.html however, SQLObject inheritance won't work in your case anyway: neither is it designed for legacy databases nor can it handle many-to-one relationships. > Since python has multiple inheritance, you can see a parallel between joins > and inheritance. Think of it like a mixin class. Not "account IS a man", > account has the properties of a man. this may mean that both account and man *have* same contact info, or that account *has* a contact person... but as far as i understand, you cannot amend the database structure, so these considerations do not apply. in your case, account has a buyer, and you cannot change that. best wishes, alex. |
From: <jws...@ra...> - 2004-02-20 04:39:58
|
>class CustomerAccount(SQLObject): > # private composition > _buyer = ForeignKey('Buyer') > > # public proxy > def _get_emailAddr(self): ^^^^^^^^^^^^^^^^^^^^ > # this could also have proxy at Buyer obj to buyerInfo, so like: > # return self._buyer.emailAddr == > return self._buyer.buyerInfo.emailAddr > > >makes this work: > >x = CustomerAccount(id) >print x.emailAddr I don't understand this magic, but it seems to be working for me. I still have to specify all my classes by hand, though. If I do a dir() on an instance of my class, I get a list of ALL private and public attributes. What is the best way for a program containing my SQLOject to get a list of the public attributes that object exposed in it's definition, excluding private and inherited items? The printable representation of the object looks like <Customer 6 name='ACME' buyerID=1> '_columns' lists the real columns, but not the proxied ones. I could also just add a method in my class to return a list of fields. I'm thinking of generating a gui form based on what the object tells me it's fields are. |
From: Ian B. <ia...@co...> - 2004-02-21 09:45:42
|
On Feb 19, 2004, at 10:33 PM, jws...@ra... wrote: > If I do a dir() on an instance of my class, I get a list of ALL private > and public attributes. What is the best way for a program containing my > SQLOject to get a list of the public attributes that object exposed in > it's definition, excluding private and inherited items? The printable > representation of the object looks like > > <Customer 6 name='ACME' buyerID=1> > > '_columns' lists the real columns, but not the proxied ones. I could > also > just add a method in my class to return a list of fields. I'm thinking > of generating a gui form based on what the object tells me it's fields > are. Well, AFAIK there's not really a good way to list all the attributes of a class in a general sense. Even hasattr (I believe) just tries to get the attribute, and if it gets an AttributeError it returns false -- there's no backdoor introspection that gives you everything. It can be a little frustrating sometimes. I'd advise that you think about this in more explicit terms -- you aren't exposing all attributes, you really want to expose specific attributes. -- Ian Bicking | ia...@co... | http://blog.ianbicking.org |
From: alexander s. <al...@an...> - 2004-02-17 19:04:23
|
jws...@ra... wrote, at 17.02.2004 19:33: >>>Note that from the standpoint of the cust_account object, these are all >>>one-to-one relations. > >>if the relations are strict one-to-one, then why data is divided into >>separate tables? > > They are actually many-to-one, but from this side of the join it looks like > a one-to-one. This customer account has that one buyer. From the buyer side > of the join, that one buyer has many potential customer accounts. well, this is a sketch of how we did attribute delegation before switching to Daniel's patch: class Buyer(SQLObject): whatever =Col() class MetaAccount(MetaSQLObject): def __new__(cls, name, bases, cdict): cdict["buyer"] = ForeignKey("buyer") return super(MetaAccount, cls).__new__(cls, name, bases, cdict) def __init__(cls, name, bases, cdict): cls._buyer_attrs = ["whatever"] super(MetaAccount, cls).__init__(name, bases, cdict) for _attr in cls._buyer_attrs: def _get(self, attr=_attr): return getattr(self.buyer, attr) def _set(self, value, attr=_attr): setattr(self.buyer, attr, value) setattr(cls, _attr, property(_get, _set)) cls._reprItems = lambda s: s.buyer._reprItems() \ + super(s.__class__, s)._reprItems() class Account(SQLObject): __metaclass__ = MetaAccount ... and if you want Account.new() to update (or create) the buyer, you need some more magic to tweak classmethod `new` as well. please note that this code is stripped down as much as possible and is not tested, but you can get the idea. however, there were some problems with this delegation, discovered by Oleg Broytmann: as far as i understand, account.whatever cannot be accessed unless at least one instance of Buyer is created. see http://sourceforge.net/mailarchive/forum.php?thread_id=3879735&forum_id=30269 >>perhaps relationships may be optional, e.g. some customers are buyers, >>and others are not? if this is the case, then you may consider using >>SQLObject inheritance patch by Daniel Savard. > > No, customers = accounts and buyers = people. Inheritance is one way of > looking at it, though. I will investigate that. perhaps not. (i do not think that you mean "account is a man" inheritance, do you?) in our application, we had an object-oriented information model, class inheritance was there from the outset. best wishes, alex. |