From: Rajeev J S. <raj...@di...> - 2005-05-09 15:19:19
|
Hi, I would like some advice from the users of SQLObject to achieve a goal. I am writing a PyQt program, which uses SQLObject to connect to the database. I would like to reuse the database integrity features of PostgreSQL as well as the notNone and unique keywords on Col subclasses. But the problem is, when a user creates a new object in the GUI client, it is entirely possible that an object will not meet these constraints. For eg, If i have a class like so: class SystemUser(SQObject): username = StringCol(length="20", notNone=True, unique=True, default="") and I have a form say, SystemUserEditor, which, when created without a SystemUser, will create a new SystemUser and allow the user to edit it. But since the default is the empty string, and the column is unique, it is possible that when 2 clients try to create a new user, one will fail. So, what would be a good strategy to create proxies of some sort, which allow the temporary situation that username is "" (or even NULL, or even any other integrity constraints) ? Is there some undocumented SQLObject mode for this ? Rajeev J Sebastian |
From: David M. C. <da...@da...> - 2005-05-09 15:50:48
|
On Mon, May 09, 2005 at 08:48:22PM +0000, Rajeev J Sebastian wrote: > But the problem is, when a user creates a new object in the GUI client, it is > entirely possible that an object will not meet these constraints. This is something that I think can make SQLObject somewhat awkward. Here's a simple-minded attempt that I've had lying about (uses sqlmeta stuff from CVS). It just returns None for any attributes. I think I have a fancier one somewhere in the boneyard. class Proxy(object): def __init__(self, soClass, selectBeforeInsert=True): self.soClass = soClass self.selectBeforeInsert = selectBeforeInsert for col in soClass.sqlmeta._columns: if col.foreignName: otherClass = getattr(soClass, '_SO_class_%s'% col.foreignKey) setattr(self, col.foreignName, Proxy(otherClass)) def set(self, **kw): if self.selectBeforeInsert: s = self.soClass.selectBy(**kw) count = s.count() assert 0 <= count <= 1 if count==1: return s[0] return self.soClass(**kw) def __getattr__(self, attr): return None def __repr__(self): return object.__repr__(self).replace('object', 'object for %s' % self.soClass.__name__) Dave Cook |
From: Rajeev J S. <raj...@di...> - 2005-05-09 16:48:05
|
On Monday 09 May 2005 3:50 pm, David M. Cook wrote: > On Mon, May 09, 2005 at 08:48:22PM +0000, Rajeev J Sebastian wrote: > > But the problem is, when a user creates a new object in the GUI client, > > it is entirely possible that an object will not meet these constraints. > > This is something that I think can make SQLObject somewhat awkward. Here's > a simple-minded attempt that I've had lying about (uses sqlmeta stuff from > CVS). It just returns None for any attributes. I think I have a fancier > one somewhere in the boneyard. Perhaps, the lazy update feature, if applied to inserts as well would be a solution ? Does SQLObject support such a thing in CVS/SVN ? Rajeev J Sebastian |
From: Ian B. <ia...@co...> - 2005-05-10 01:33:14
|
David M. Cook wrote: > On Mon, May 09, 2005 at 08:48:22PM +0000, Rajeev J Sebastian wrote: > > >>But the problem is, when a user creates a new object in the GUI client, it is >>entirely possible that an object will not meet these constraints. > > > This is something that I think can make SQLObject somewhat awkward. Here's > a simple-minded attempt that I've had lying about (uses sqlmeta stuff from > CVS). It just returns None for any attributes. I think I have a fancier > one somewhere in the boneyard. After discussing it with Rajeev on IRC (at least I think it was him) I think I understand a little bit better what my own patterns really are. When I deal with objects that I don't want to manipulate, but I want to give to the user, I always serialize them into dictionaries (typically using ad hoc code) and then the user manipulates the dictionaries, then I apply those changes. After thinking about it, I think this is really a kind of command system, where the dictionaries taken from the SQLObject instances ({} for a new object) are like templates for a command; finally the command is applied, kind of like a "commit", except one that happens entirely outside of the database. Personally I find commands easier to think about in terms of UI (even if I don't actually think of them as commands -- but maybe I will now). They are certainly easier to validate, and easier to store and throw away. It's also something that could probably be better represented as an actual object -- separate from SQLObject instances, but obviously useful with them. I just represent them as dictionaries, which lacks a certain finesse. Whenever I otherwise consider staging objects, I always come back to transactions, except transactions not implemented in the database. And that's just too hard. I think a naive implementation would be missing many parts that would quickly become necessary, and would only work for trivial applications. > class Proxy(object): > > def __init__(self, soClass, selectBeforeInsert=True): > self.soClass = soClass > self.selectBeforeInsert = selectBeforeInsert > for col in soClass.sqlmeta._columns: > if col.foreignName: > otherClass = getattr(soClass, '_SO_class_%s'% col.foreignKey) > setattr(self, col.foreignName, Proxy(otherClass)) > > def set(self, **kw): > if self.selectBeforeInsert: > s = self.soClass.selectBy(**kw) > count = s.count() > assert 0 <= count <= 1 > if count==1: > return s[0] > return self.soClass(**kw) > > def __getattr__(self, attr): > return None > > def __repr__(self): > return object.__repr__(self).replace('object', > 'object for %s' > % self.soClass.__name__) I'm not entirely clear what this is for. At first I thought it was a kind of optimistic lock, but not so. How would you actually use this? -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: David M. C. <da...@da...> - 2005-05-10 05:59:09
|
On Mon, May 09, 2005 at 08:33:58PM -0500, Ian Bicking wrote: > >class Proxy(object): > I'm not entirely clear what this is for. Uniform object access. >At first I thought it was a > kind of optimistic lock, but not so. How would you actually use this? In some ORMs or OODBs objects start out as 'unmanaged' or 'lonely', and then you save them. For example, in IndexedCatalog catalog = shelf[Customer] # create an 'empty' object obj = Customer() # get some values for display (values are None at this point, of course) name = obj.name phone = obj.phone # set some values obj.name = 'Bob' # obj is 'lonely' up to this point catalog.insert(obj) So the obj doesn't have to meet integrity constraints until you actually insert it, but you can still use it the same way you would use a 'managed' object. The idea of the Proxy is to mimic this behavior: obj = Proxy(Customer) # pass this single object off to my input handler # get some values for display name = obj.name phone = obj.phone # set some values obj.set(**data) Though the above simple-minded proxy is not really up to the task when you need to create foreign key objects first. Essentially Proxy just encapsulates logic like # get some values for display foo = getattr(obj, 'foo', None) # obj may be None baz = getattr(obj, 'baz', None) # now do something with input values if not obj: obj = soClass(**data) else: obj.set(**data) Dave Cook |