📄 properties.py
字号:
self.foreign_keys.add(binary.left) self._opposite_side.add(binary.right) for f in binary.right.foreign_keys: if f.references(binary.left.table): self.foreign_keys.add(binary.right) self._opposite_side.add(binary.left) visitors.traverse(self.primaryjoin, visit_binary=visit_binary) if len(self.foreign_keys) == 0: raise exceptions.ArgumentError( "Can't locate any foreign key columns in primary join " "condition '%s' for relationship '%s'. Specify " "'foreign_keys' argument to indicate which columns in " "the join condition are foreign." %(str(self.primaryjoin), str(self))) if self.secondaryjoin is not None: visitors.traverse(self.secondaryjoin, visit_binary=visit_binary) def _determine_direction(self): """Determine our *direction*, i.e. do we represent one to many, many to many, etc. """ if self.secondaryjoin is not None: self.direction = sync.MANYTOMANY elif self._is_self_referential(): # for a self referential mapper, if the "foreignkey" is a single or composite primary key, # then we are "many to one", since the remote site of the relationship identifies a singular entity. # otherwise we are "one to many". if self._legacy_foreignkey: for f in self._legacy_foreignkey: if not f.primary_key: self.direction = sync.ONETOMANY else: self.direction = sync.MANYTOONE elif self.remote_side: for f in self.foreign_keys: if f in self.remote_side: self.direction = sync.ONETOMANY return else: self.direction = sync.MANYTOONE else: self.direction = sync.ONETOMANY else: for mappedtable, parenttable in [(self.mapper.mapped_table, self.parent.mapped_table), (self.mapper.local_table, self.parent.local_table)]: onetomany = len([c for c in self.foreign_keys if mappedtable.c.contains_column(c)]) manytoone = len([c for c in self.foreign_keys if parenttable.c.contains_column(c)]) if not onetomany and not manytoone: raise exceptions.ArgumentError( "Can't determine relation direction for relationship '%s' " "- foreign key columns are present in neither the " "parent nor the child's mapped tables" %(str(self))) elif onetomany and manytoone: continue elif onetomany: self.direction = sync.ONETOMANY break elif manytoone: self.direction = sync.MANYTOONE break else: raise exceptions.ArgumentError( "Can't determine relation direction for relationship '%s' " "- foreign key columns are present in both the parent and " "the child's mapped tables. Specify 'foreign_keys' " "argument." % (str(self))) def _determine_remote_side(self): if not self.remote_side: if self.direction is sync.MANYTOONE: self.remote_side = util.Set(self._opposite_side) elif self.direction is sync.ONETOMANY or self.direction is sync.MANYTOMANY: self.remote_side = util.Set(self.foreign_keys) self.local_side = util.Set(self._opposite_side).union(util.Set(self.foreign_keys)).difference(self.remote_side) def _create_polymorphic_joins(self): # get ready to create "polymorphic" primary/secondary join clauses. # these clauses represent the same join between parent/child tables that the primary # and secondary join clauses represent, except they reference ColumnElements that are specifically # in the "polymorphic" selectables. these are used to construct joins for both Query as well as # eager loading, and also are used to calculate "lazy loading" clauses. if self.loads_polymorphic: # as we will be using the polymorphic selectables (i.e. select_table argument to Mapper) to figure this out, # first create maps of all the "equivalent" columns, since polymorphic selectables will often munge # several "equivalent" columns (such as parent/child fk cols) into just one column. target_equivalents = self.mapper._equivalent_columns if self.secondaryjoin: self.polymorphic_secondaryjoin = ClauseAdapter(self.mapper.select_table).traverse(self.secondaryjoin, clone=True) self.polymorphic_primaryjoin = self.primaryjoin else: if self.direction is sync.ONETOMANY: self.polymorphic_primaryjoin = ClauseAdapter(self.mapper.select_table, include=self.foreign_keys, equivalents=target_equivalents).traverse(self.primaryjoin, clone=True) elif self.direction is sync.MANYTOONE: self.polymorphic_primaryjoin = ClauseAdapter(self.mapper.select_table, exclude=self.foreign_keys, equivalents=target_equivalents).traverse(self.primaryjoin, clone=True) self.polymorphic_secondaryjoin = None # load "polymorphic" versions of the columns present in "remote_side" - this is # important for lazy-clause generation which goes off the polymorphic target selectable for c in list(self.remote_side): if self.secondary and self.secondary.columns.contains_column(c): continue for equiv in [c] + (c in target_equivalents and list(target_equivalents[c]) or []): corr = self.mapper.select_table.corresponding_column(equiv) if corr: self.remote_side.add(corr) break else: raise exceptions.AssertionError(str(self) + ": Could not find corresponding column for " + str(c) + " in selectable " + str(self.mapper.select_table)) else: self.polymorphic_primaryjoin = self.primaryjoin self.polymorphic_secondaryjoin = self.secondaryjoin def _post_init(self): if logging.is_info_enabled(self.logger): self.logger.info(str(self) + " setup primary join " + str(self.primaryjoin)) self.logger.info(str(self) + " setup polymorphic primary join " + str(self.polymorphic_primaryjoin)) self.logger.info(str(self) + " setup secondary join " + str(self.secondaryjoin)) self.logger.info(str(self) + " setup polymorphic secondary join " + str(self.polymorphic_secondaryjoin)) self.logger.info(str(self) + " foreign keys " + str([str(c) for c in self.foreign_keys])) self.logger.info(str(self) + " remote columns " + str([str(c) for c in self.remote_side])) self.logger.info(str(self) + " relation direction " + (self.direction is sync.ONETOMANY and "one-to-many" or (self.direction is sync.MANYTOONE and "many-to-one" or "many-to-many"))) if self.uselist is None and self.direction is sync.MANYTOONE: self.uselist = False if self.uselist is None: self.uselist = True if not self.viewonly: self._dependency_processor = dependency.create_dependency_processor(self) # primary property handler, set up class attributes if self.is_primary(): # if a backref name is defined, set up an extension to populate # attributes in the other direction if self.backref is not None: self.attributeext = self.backref.get_extension() if self.backref is not None: self.backref.compile(self) elif not mapper.class_mapper(self.parent.class_, compile=False)._get_property(self.key, raiseerr=False): raise exceptions.ArgumentError("Attempting to assign a new relation '%s' to a non-primary mapper on class '%s'. New relations can only be added to the primary mapper, i.e. the very first mapper created for class '%s' " % (self.key, self.parent.class_.__name__, self.parent.class_.__name__)) super(PropertyLoader, self).do_init() def _is_self_referential(self): return self.parent.mapped_table is self.target or self.parent.select_table is self.target def primary_join_against(self, mapper, selectable=None): return self.__cached_join_against(mapper, selectable, True, False) def secondary_join_against(self, mapper): return self.__cached_join_against(mapper, None, False, True) def full_join_against(self, mapper, selectable=None): return self.__cached_join_against(mapper, selectable, True, True) def __cached_join_against(self, mapper, selectable, primary, secondary): if selectable is None: selectable = mapper.local_table try: rec = self.__parent_join_cache[selectable] except KeyError: self.__parent_join_cache[selectable] = rec = {} key = (mapper, primary, secondary) if key in rec: return rec[key] parent_equivalents = mapper._equivalent_columns if primary: if selectable is not mapper.local_table: if self.direction is sync.ONETOMANY: primaryjoin = ClauseAdapter(selectable, exclude=self.foreign_keys, equivalents=parent_equivalents).traverse(self.polymorphic_primaryjoin) elif self.direction is sync.MANYTOONE: primaryjoin = ClauseAdapter(selectable, include=self.foreign_keys, equivalents=parent_equivalents).traverse(self.polymorphic_primaryjoin) elif self.secondaryjoin: primaryjoin = ClauseAdapter(selectable, exclude=self.foreign_keys, equivalents=parent_equivalents).traverse(self.polymorphic_primaryjoin) else: primaryjoin = self.polymorphic_primaryjoin if secondary: secondaryjoin = self.polymorphic_secondaryjoin rec[key] = ret = primaryjoin & secondaryjoin else: rec[key] = ret = primaryjoin return ret elif secondary: rec[key] = ret = self.polymorphic_secondaryjoin return ret else: raise AssertionError("illegal condition") def get_join(self, parent, primary=True, secondary=True, polymorphic_parent=True): """deprecated. use primary_join_against(), secondary_join_against(), full_join_against()""" if primary and secondary: return self.full_join_against(parent, parent.select_table) elif primary: return self.primary_join_against(parent, parent.select_table) elif secondary: return self.secondary_join_against(parent) else: raise AssertionError("illegal condition") def register_dependencies(self, uowcommit): if not self.viewonly: self._dependency_processor.register_dependencies(uowcommit)PropertyLoader.logger = logging.class_logger(PropertyLoader)class BackRef(object): """Attached to a PropertyLoader to indicate a complementary reverse relationship. Can optionally create the complementing PropertyLoader if one does not exist already.""" def __init__(self, key, _prop=None, **kwargs): self.key = key self.kwargs = kwargs self.prop = _prop def compile(self, prop): if self.prop: return self.prop = prop mapper = prop.mapper.primary_mapper() if mapper._get_property(self.key, raiseerr=False) is None: pj = self.kwargs.pop('primaryjoin', None) sj = self.kwargs.pop('secondaryjoin', None) parent = prop.parent.primary_mapper() self.kwargs.setdefault('viewonly', prop.viewonly) self.kwargs.setdefault('post_update', prop.post_update) relation = PropertyLoader(parent, prop.secondary, pj, sj, backref=BackRef(prop.key, _prop=prop), is_backref=True, **self.kwargs) mapper._compile_property(self.key, relation); prop._reverse_property = mapper._get_property(self.key) mapper._get_property(self.key)._reverse_property = prop else: raise exceptions.ArgumentError("Error creating backref '%s' on relation '%s': property of that name exists on mapper '%s'" % (self.key, prop, mapper)) def get_extension(self): """Return an attribute extension to use with this backreference.""" return attributes.GenericBackrefExtension(self.key)mapper.ColumnProperty = ColumnPropertymapper.SynonymProperty = SynonymProperty
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -