Thread: [SQLObject] Using SQLObjects as Abstract Classes
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: Brad B. <br...@bb...> - 2003-07-24 19:41:27
|
Hi all, How can I use an SQLObject-derived class as an abstract base class? E.g. class Foo(SQLObject): bar = IntCol() def abstractMethod(self): raise NotImplementedError("Override me.") class Bar(Foo): def abstractMethod(self): print self.bar If I now do: b = Bar.new(bar = 1) b.abstractMethod() this will break, with an error message (traceback not shown): psycopg.ProgrammingError: ERROR: Relation "bar" does not exist If I add _table = 'foo' to Foo, the same error results. If I add _table = 'foo' to Bar instead, it Does The Right Thing, however this is ugly. Bar will never actually create a table, I just want to be able to define the common functionality and (of course) the data mapping in the base class Foo, and then override and extend in Bar, whilst having my properties magically save themselves into the database. How do I lay this out then? Regards, -- Brad Bollenbach BBnet.ca |
From: Ian B. <ia...@co...> - 2003-07-25 01:56:49
|
On Thu, 2003-07-24 at 14:42, Brad Bollenbach wrote: > Hi all, > > How can I use an SQLObject-derived class as an abstract base class? Hmm... I'm not exactly clear what an abstract base class is. Is there a particular problem you are trying to solve? The problem with two classes for one table is that it's very ambiguous. Generally a row is an instance, and an instance is a row, very one-to-one. That gets very mucky with multiple classes for one table. I feel like this should be possible to handle in one class, even if it may not feel as object-oriented (but it can probably be just as polymorphic, which is the better half of OO, even if it doesn't have a "proper" class hierarchy, which isn't very important). Ian |
From: Brad B. <br...@bb...> - 2003-07-25 13:21:05
|
On Thu, Jul 24, 2003 at 08:57:19PM -0500, Ian Bicking wrote: > On Thu, 2003-07-24 at 14:42, Brad Bollenbach wrote: > > Hi all, > > > > How can I use an SQLObject-derived class as an abstract base class? > > Hmm... I'm not exactly clear what an abstract base class is. Is there a > particular problem you are trying to solve? An abstract base class is a class that's not intended to be directly instantiated, instead defining methods that are intended to be overridden in derived classes. The problem I'm trying to solve is a web monitoring system: MonitorBase | | | | | -> StatusMonitor | --> KeywordMonitor ----> MD5ChecksumMonitor So, I want to be able to do something like (untested code): class MonitorBase(SQLObject): """I'm the base class from which all monitors shall be derived. """ created_date = DateTimeCol(notNone = True) last_run_date = DateTimeCol(notNone = True) active = IntCol() ... def doMonitorCheck(self): """Run the check for this monitor.""" raise NotImplementedError("doMonitorCheck is an abstract method.") class StatusMonitor(MonitorBase): """I monitor websites for the 200 OK status.""" def doMonitorCheck(self): ...logic to check for 200 OK... Except this breaks, because any property access on StatusMonitor tries to look for a table called status_monitor. Unless I specify _table = 'monitor' in every derived class, which seems a bit clunky. What would make sense here? Am I approaching this incorrectly or should there be some way of marking SQLObject classes as abstract? It seems to me that if SQLObject wants to support truly transparent persistence then it should allow me to layout my classes without getting in the way (or getting clunky). -- Brad Bollenbach BBnet.ca |
From: Ian B. <ia...@co...> - 2003-07-25 16:08:07
|
On Fri, 2003-07-25 at 08:22, Brad Bollenbach wrote: > On Thu, Jul 24, 2003 at 08:57:19PM -0500, Ian Bicking wrote: > > On Thu, 2003-07-24 at 14:42, Brad Bollenbach wrote: > > > Hi all, > > > > > > How can I use an SQLObject-derived class as an abstract base class? > > > > Hmm... I'm not exactly clear what an abstract base class is. Is there a > > particular problem you are trying to solve? > > An abstract base class is a class that's not intended to be directly > instantiated, instead defining methods that are intended to be > overridden in derived classes. Oh, right right, I don't know why the terminology was confusing me. You'll have to do something like: class MonitorBase: ... class Monitor(SQLObject, MonitorBase): ... > The problem I'm trying to solve is a web monitoring system: > > MonitorBase > | | | > | | -> StatusMonitor > | --> KeywordMonitor > ----> MD5ChecksumMonitor > > So, I want to be able to do something like (untested code): > > class MonitorBase(SQLObject): > """I'm the base class from which all monitors shall > be derived. > """ > > created_date = DateTimeCol(notNone = True) > last_run_date = DateTimeCol(notNone = True) > active = IntCol() > ... > > def doMonitorCheck(self): > """Run the check for this monitor.""" > raise NotImplementedError("doMonitorCheck is an abstract method.") > > > class StatusMonitor(MonitorBase): > """I monitor websites for the 200 OK status.""" > > def doMonitorCheck(self): > ...logic to check for 200 OK... > > Except this breaks, because any property access on StatusMonitor tries > to look for a table called status_monitor. Unless I specify _table = > 'monitor' in every derived class, which seems a bit clunky. If you have that class layout, you are supposing that there will be other subclasses of Monitor. However, each of these subclasses must be a separate table, or great confusion will ensue. Python inheritance simply doesn't map to RDBMS tables. There are things that can be called inheritance among RDBMS tables, but they aren't the same thing. So SQLObject just takes a one-class-one-table approach. You can still use inheritance, and a class can be abstract so long as you don't use it, but it's still one-class-one-table. It's improper to use _table = 'monitor' in each class, because that breaks that rule. If you have one table you shouldn't have more than one class that you use with that table. Ian |
From: Brad B. <br...@bb...> - 2003-07-25 17:26:14
|
On Fri, Jul 25, 2003 at 11:08:36AM -0500, Ian Bicking wrote: > On Fri, 2003-07-25 at 08:22, Brad Bollenbach wrote: > > On Thu, Jul 24, 2003 at 08:57:19PM -0500, Ian Bicking wrote: > > > On Thu, 2003-07-24 at 14:42, Brad Bollenbach wrote: > > > > Hi all, > > > > > > > > How can I use an SQLObject-derived class as an abstract base class? > > > > > > Hmm... I'm not exactly clear what an abstract base class is. Is there a > > > particular problem you are trying to solve? > > > > An abstract base class is a class that's not intended to be directly > > instantiated, instead defining methods that are intended to be > > overridden in derived classes. > > Oh, right right, I don't know why the terminology was confusing me. > > You'll have to do something like: > > class MonitorBase: > ... > > class Monitor(SQLObject, MonitorBase): > ... This still doesn't solve the problem though. I would have to do it more like: class StatusMonitor(SQLObject, MonitorBase) class KeywordMonitor(SQLObject, MonitorBase) etc (which still doesn't quite work, because I'm getting separate tables for each, which I don't want) Eeek. > > The problem I'm trying to solve is a web monitoring system: > > > > MonitorBase > > | | | > > | | -> StatusMonitor > > | --> KeywordMonitor > > ----> MD5ChecksumMonitor [snip] > If you have that class layout, you are supposing that there will be > other subclasses of Monitor. However, each of these subclasses must be > a separate table, or great confusion will ensue. There will only be a single monitor table. Monitors store a common set of properties. The only thing that's different is how they behave when you say "do your monitor check". > Python inheritance simply doesn't map to RDBMS tables. There are things > that can be called inheritance among RDBMS tables, but they aren't the > same thing. So SQLObject just takes a one-class-one-table approach. > You can still use inheritance, and a class can be abstract so long as > you don't use it, but it's still one-class-one-table. It's improper to > use _table = 'monitor' in each class, because that breaks that rule. If > you have one table you shouldn't have more than one class that you use > with that table. Hrmph. I can see where you're coming from, but perhaps some thought could be spent on figuring out how to make that "one class" (per table) be an abstract class (so, I kind of really only do have class per table, because StatusMonitor, KeywordMonitor, etc are just there to specify differences in behaviour, not in attributes. That "one class" is my abstract base.) FWIW, the architecture of this app may change so that each monitor has its own table (/maybe/), but in any case, I can see many uses for abstract base SQLObjects. What do you guys think about using abstracts as SQLObjects? Can there be a solution? -- Brad Bollenbach BBnet.ca |
From: Ian B. <ia...@co...> - 2003-07-25 18:18:41
|
On Fri, 2003-07-25 at 12:27, Brad Bollenbach wrote: > > If you have that class layout, you are supposing that there will be > > other subclasses of Monitor. However, each of these subclasses must be > > a separate table, or great confusion will ensue. > > There will only be a single monitor table. Monitors store a common set > of properties. The only thing that's different is how they behave when > you say "do your monitor check". Then make that into a multiple-class object, Checker. Like: class Checker(object): def __init__(self, monitor): self.monitor = monitor def check(self): assert 0, "Subclass responsibility" class HTTPChecker(Checker): def check(self): ... class MD5HTTPChecker(HTTPChecker): def check(self): ... checkers = {'http': HTTPChecker, 'md5': MD5HTTPChecker} class Monitor(SQLObject): monitorType = StringCol(length=10) def _get_checker(self): return checkers[self.monitorType](self) Voila! Composition beats inheritance! > > Python inheritance simply doesn't map to RDBMS tables. There are things > > that can be called inheritance among RDBMS tables, but they aren't the > > same thing. So SQLObject just takes a one-class-one-table approach. > > You can still use inheritance, and a class can be abstract so long as > > you don't use it, but it's still one-class-one-table. It's improper to > > use _table = 'monitor' in each class, because that breaks that rule. If > > you have one table you shouldn't have more than one class that you use > > with that table. > > Hrmph. > > I can see where you're coming from, but perhaps some thought could be > spent on figuring out how to make that "one class" (per table) be an > abstract class (so, I kind of really only do have class per table, > because StatusMonitor, KeywordMonitor, etc are just there to specify > differences in behaviour, not in attributes. That "one class" is my > abstract base.) It just wouldn't work. What are you going to do, fetch a generic Monitor instance, check its type, then refetch a specific instance? Or do a direct query to find the type, then instantiate the object with the correct class? It just doesn't make sense. It also is going to cause all sorts of weird structural problems when you do need more tables. With the above technique you can add tables fairly easily. Say you want to create a md5_checker table (to store the MD5 hash). class MD5Checker(HTTPChecker, SQLObject): def setMonitor(self, monitor): HTTPChecker.__init__(self, monitor) md5 = StringCol(length=128) function md5CheckerGetter(self, monitor): inst = MD5Checker.selectBy(monitor=monitor)[0] inst.setMonitor(monitor) return inst checkers['md5'] = md5CheckerGetter You don't have to create tables for every checker, just the one that needs it. You don't have to fiddle with inheritance of the Monitor class. Someon, who I can't remember, and who's link I've lost, gave a link to a good article on inheritance and how to map them. The stuff I've written above follows one of them (one table for each class, including abstract classes), while standard inheritance in SQLObject is one table per concrete class. Implementing the former directly in SQLObject is, IMHO, a lot of work for nothing, since multiple classes is superior in Python just like multiple tables are superior in the database (IMNSHO). It may not seem easier to implement (though I don't think it's any harder), but I think it will be much easier to maintain, and result in more readable code. Ian |
From: Brad B. <br...@bb...> - 2003-07-25 19:29:20
|
On Fri, Jul 25, 2003 at 01:19:02PM -0500, Ian Bicking wrote: > On Fri, 2003-07-25 at 12:27, Brad Bollenbach wrote: > > > If you have that class layout, you are supposing that there will be > > > other subclasses of Monitor. However, each of these subclasses must be > > > a separate table, or great confusion will ensue. > > > > There will only be a single monitor table. Monitors store a common set > > of properties. The only thing that's different is how they behave when > > you say "do your monitor check". > > Then make that into a multiple-class object, Checker. Like: > > class Checker(object): > def __init__(self, monitor): > self.monitor = monitor > def check(self): > assert 0, "Subclass responsibility" > class HTTPChecker(Checker): > def check(self): ... > class MD5HTTPChecker(HTTPChecker): > def check(self): ... > > checkers = {'http': HTTPChecker, 'md5': MD5HTTPChecker} > > class Monitor(SQLObject): > monitorType = StringCol(length=10) > def _get_checker(self): > return checkers[self.monitorType](self) > > > Voila! Composition beats inheritance! Indeed, this looks like the best way to do it (whilst avoiding any ugly hacks). Thanks. :) -- Brad Bollenbach BBnet.ca |