From: Randall S. <ra...@tn...> - 2006-01-05 22:07:08
|
"""This is sample code for SQLObject relation awareness. I've had this itch for a long time and I scratched it today. I think that a major strength of an OR Mapper is its awareness of the relationships between data objects. It should be possible to ask an object how it is related to another object and for the related data regardless of how distantly related they are. Currently (from what I've seen), this is only easy to do if two objects are directly related. The sample code below adds a method that attempts to retrieve data from another class if it is related. It is limited to SOMultipleJoin relationships and it takes the first path it finds, but it should give you an idea of what I've been dreaming about. Ultimately, I think an object should be able to map out and follow all paths to all related objects. I would enjoy some feedback on this. -- Randall """ import pkg_resources pkg_resources.require("sqlobject > 0.7") from sqlobject import * from sqlobject.joins import SOMultipleJoin from sqlobject.sqlbuilder import INNERJOINOn sqlhub.threadConnection = connectionForURI('sqlite:/:memory:') def mJoins(some_joins): return filter(lambda x: isinstance(x, SOMultipleJoin), some_joins) def recurseJoins(rpath, end_class, m_join): rpath.append(m_join) next_class = m_join.otherClass next_m_joins = mJoins(next_class.sqlmeta.joins) for next_m_join in next_m_joins: if next_class == end_class: break recurseJoins(rpath, end_class, next_m_join) return rpath class SSQLObject(SQLObject): def getRelated(thisklass, otherklass): """Find a path via MultipleJoin and return results. Find a path from thisklass to otherklass via instances of SOMultipleJoin and return the results. Just take the first path that comes along. """ all_joins = thisklass.sqlmeta.joins[:] all_joins.extend(otherklass.sqlmeta.joins) start_joins = mJoins(all_joins) if not start_joins: raise Exception, 'No join to start with.' for start_join in start_joins: start_class = start_join.soClass if start_class == thisklass: end_class = klass else: end_class = thisklass rpath = recurseJoins([], end_class, start_join) if rpath[-1].otherClass == end_class: # We succeeded. break else: # Try again. rpath = [] if not rpath: return None # Now that we have a path, get the data. exprs = [] relations = rpath for relation in relations: k1 = relation.otherClass k2 = relation.soClass dbToPy = k1.sqlmeta.style.dbColumnToPythonAttr jcol1 = k2.q.id jcol2 = getattr(k1.q, dbToPy(relation.joinColumn)) expr = INNERJOINOn(None, k1, jcol1 == jcol2) exprs.append(expr) return thisklass.select(join=exprs) getRelated = classmethod(getRelated) class A(SSQLObject): name = StringCol() bs = MultipleJoin('B') class B(SSQLObject): name = StringCol() a = ForeignKey('A') bs = MultipleJoin('C') class C(SSQLObject): name = StringCol() b = ForeignKey('B') for klass in (A, B, C): klass.createTable() a1 = A(name='John') a2 = A(name='Jane') b1 = B(name='Jack', a=a1) b2 = B(name='Jill', a=a2) c1 = C(name='Mary', b=b1) c2 = C(name='Sue', b=b2) print list(A.getRelated(B)) print list(B.getRelated(C)) print list(A.getRelated(C)) |