From: Jonathan LaCour Date: Tue, 6 Jun 2006 17:58:41 +0000 (+0000) Subject: Further improved the process_relationships function to handle the ordering of X-Git-Tag: rel_0_2_3~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=969d244f1f3114f579adaf9a2a3477f5066d9d30;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Further improved the process_relationships function to handle the ordering of class definitions better. The function was only looking at relationships, not foreign keys, and was making some improper assumptions. The unit tests all still pass, and now some of my own code actually works, regardless of the order that I define the classes in.' 'lib/sqlalchemy/ext/activemapper.py --- diff --git a/lib/sqlalchemy/ext/activemapper.py b/lib/sqlalchemy/ext/activemapper.py index 4ff7a150b6..24360f3f40 100644 --- a/lib/sqlalchemy/ext/activemapper.py +++ b/lib/sqlalchemy/ext/activemapper.py @@ -1,4 +1,5 @@ -from sqlalchemy import create_session, relation, mapper, join, DynamicMetaData, class_mapper, util +from sqlalchemy import (create_session, relation, mapper, join, + DynamicMetaData, class_mapper, util) from sqlalchemy import and_, or_ from sqlalchemy import Table, Column, ForeignKey from sqlalchemy.ext.sessioncontext import SessionContext @@ -32,8 +33,8 @@ objectstore = Objectstore(create_session) class column(object): def __init__(self, coltype, colname=None, foreign_key=None, primary_key=False, *args, **kwargs): - if isinstance( foreign_key, basestring ): - foreign_key= ForeignKey( foreign_key ) + if isinstance(foreign_key, basestring): + foreign_key = ForeignKey(foreign_key) self.coltype = coltype self.colname = colname self.foreign_key = foreign_key @@ -56,15 +57,18 @@ class relationship(object): self.secondary = secondary class one_to_many(relationship): - def __init__(self, classname, colname=None, backref=None, private=False, lazy=True): - relationship.__init__(self, classname, colname, backref, private, lazy, uselist=True) - + def __init__(self, classname, colname=None, backref=None, private=False, + lazy=True): + relationship.__init__(self, classname, colname, backref, private, + lazy, uselist=True) class one_to_one(relationship): - def __init__(self, classname, colname=None, backref=None, private=False, lazy=True): + def __init__(self, classname, colname=None, backref=None, private=False, + lazy=True): if backref is not None: backref = create_backref(backref, uselist=False) - relationship.__init__(self, classname, colname, backref, private, lazy, uselist=False) + relationship.__init__(self, classname, colname, backref, private, + lazy, uselist=False) class many_to_many(relationship): def __init__(self, classname, secondary, backref=None, lazy=True): @@ -79,14 +83,44 @@ class many_to_many(relationship): # up if the classes aren't specified in a proper order # -__deferred_classes__ = [] +__deferred_classes__ = set() +__processed_classes__ = set() def process_relationships(klass, was_deferred=False): + # first, we loop through all of the relationships defined on the + # class, and make sure that the related class already has been + # completely processed and defer processing if it has not defer = False for propname, reldesc in klass.relations.items(): - if not reldesc.classname in ActiveMapperMeta.classes: - if not was_deferred: __deferred_classes__.append(klass) + found = False + for other_klass in __processed_classes__: + if reldesc.classname == other_klass.__name__: + found = True + break + + if not found: + if not was_deferred: __deferred_classes__.add(klass) defer = True + break + # next, we loop through all the columns looking for foreign keys + # and make sure that we can find the related tables (they do not + # have to be processed yet, just defined), and we defer if we are + # not able to find any of the related tables + for col in klass.columns: + if col.foreign_key is not None: + found = False + for other_klass in ActiveMapperMeta.classes.values(): + table_name = col.foreign_key._colspec.rsplit('.', 1)[0] + if other_klass.table.fullname.lower() == table_name.lower(): + found = True + + if not found: + if not was_deferred: __deferred_classes__.add(klass) + defer = True + break + + # if we are able to find all related and referred to tables, then + # we can go ahead and assign the relationships to the class if not defer: relations = {} for propname, reldesc in klass.relations.items(): @@ -97,15 +131,24 @@ def process_relationships(klass, was_deferred=False): private=reldesc.private, lazy=reldesc.lazy, uselist=reldesc.uselist) + class_mapper(klass).add_properties(relations) - #assign_mapper(objectstore, klass, klass.table, properties=relations, - # inherits=getattr(klass, "_base_mapper", None)) - if was_deferred: __deferred_classes__.remove(klass) + if klass in __deferred_classes__: + __deferred_classes__.remove(klass) + __processed_classes__.add(klass) + # finally, loop through the deferred classes and attempt to process + # relationships for them if not was_deferred: - for deferred_class in __deferred_classes__: - process_relationships(deferred_class, was_deferred=True) - + # loop through the list of deferred classes, processing the + # relationships, until we can make no more progress + last_count = len(__deferred_classes__) + 1 + while last_count > len(__deferred_classes__): + last_count = len(__deferred_classes__) + deferred = __deferred_classes__.copy() + for deferred_class in deferred: + if deferred_class == klass: continue + process_relationships(deferred_class, was_deferred=True) class ActiveMapperMeta(type): @@ -115,7 +158,8 @@ class ActiveMapperMeta(type): table_name = clsname.lower() columns = [] relations = {} - _metadata = getattr( sys.modules[cls.__module__], "__metadata__", metadata ) + _metadata = getattr(sys.modules[cls.__module__], + "__metadata__", metadata) if 'mapping' in dict: members = inspect.getmembers(dict.get('mapping')) @@ -147,13 +191,18 @@ class ActiveMapperMeta(type): if isinstance(value, relationship): relations[name] = value + assert _metadata is not None, "No MetaData specified" + ActiveMapperMeta.metadatas.add(_metadata) cls.table = Table(table_name, _metadata, *columns) + cls.columns = columns + # check for inheritence - if hasattr( bases[0], "mapping" ): + if hasattr(bases[0], "mapping"): cls._base_mapper= bases[0].mapper - assign_mapper(objectstore, cls, cls.table, inherits=cls._base_mapper) + assign_mapper(objectstore, cls, cls.table, + inherits=cls._base_mapper) else: assign_mapper(objectstore, cls, cls.table) cls.relations = relations @@ -164,6 +213,7 @@ class ActiveMapperMeta(type): super(ActiveMapperMeta, cls).__init__(clsname, bases, dict) + class ActiveMapper(object): __metaclass__ = ActiveMapperMeta @@ -179,7 +229,7 @@ class ActiveMapper(object): def create_tables(): for metadata in ActiveMapperMeta.metadatas: metadata.create_all() + def drop_tables(): for metadata in ActiveMapperMeta.metadatas: - metadata.drop_all() - + metadata.drop_all() \ No newline at end of file