From: Mike Bayer Date: Thu, 5 Jul 2012 14:19:59 +0000 (-0400) Subject: - [bug] ORM will perform extra effort to determine X-Git-Tag: rel_0_8_0b1~336 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b297b40fca923a03e3c34094e5298d6524944c39;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - [bug] ORM will perform extra effort to determine that an FK dependency between two tables is not significant during flush if the tables are related via joined inheritance and the FK dependency is not part of the inherit_condition, saves the user a use_alter directive. [ticket:2527] --- diff --git a/CHANGES b/CHANGES index 0f0159894f..19e4ad5123 100644 --- a/CHANGES +++ b/CHANGES @@ -58,6 +58,14 @@ underneath "0.7.xx". approach can be upgraded to this new approach. [ticket:1401] + - [bug] ORM will perform extra effort to determine + that an FK dependency between two tables is + not significant during flush if the tables + are related via joined inheritance and the FK + dependency is not part of the inherit_condition, + saves the user a use_alter directive. + [ticket:2527] + - [feature] New standalone function with_polymorphic() provides the functionality of query.with_polymorphic() in a standalone form. It can be applied to any diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 761e1af569..dceccf70f8 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -206,16 +206,16 @@ class Mapper(object): local_table = None """The :class:`.Selectable` which this :class:`.Mapper` manages. - Typically is an instance of :class:`.Table` or :class:`.Alias`. - May also be ``None``. + Typically is an instance of :class:`.Table` or :class:`.Alias`. + May also be ``None``. The "local" table is the - selectable that the :class:`.Mapper` is directly responsible for + selectable that the :class:`.Mapper` is directly responsible for managing from an attribute access and flush perspective. For non-inheriting mappers, the local table is the same as the "mapped" table. For joined-table inheritance mappers, local_table will be the particular sub-table of the overall "join" which - this :class:`.Mapper` represents. If this mapper is a + this :class:`.Mapper` represents. If this mapper is a single-table inheriting mapper, local_table will be ``None``. See also :attr:`~.Mapper.mapped_table`. @@ -225,11 +225,11 @@ class Mapper(object): mapped_table = None """The :class:`.Selectable` to which this :class:`.Mapper` is mapped. - Typically an instance of :class:`.Table`, :class:`.Join`, or + Typically an instance of :class:`.Table`, :class:`.Join`, or :class:`.Alias`. - The "mapped" table is the selectable that - the mapper selects from during queries. For non-inheriting + The "mapped" table is the selectable that + the mapper selects from during queries. For non-inheriting mappers, the mapped table is the same as the "local" table. For joined-table inheritance mappers, mapped_table references the full :class:`.Join` representing full rows for this particular @@ -241,7 +241,7 @@ class Mapper(object): """ inherits = None - """References the :class:`.Mapper` which this :class:`.Mapper` + """References the :class:`.Mapper` which this :class:`.Mapper` inherits from, if any. This is a *read only* attribute determined during mapper construction. @@ -260,7 +260,7 @@ class Mapper(object): """ concrete = None - """Represent ``True`` if this :class:`.Mapper` is a concrete + """Represent ``True`` if this :class:`.Mapper` is a concrete inheritance mapper. This is a *read only* attribute determined during mapper construction. @@ -283,7 +283,7 @@ class Mapper(object): primary_key = None """An iterable containing the collection of :class:`.Column` objects - which comprise the 'primary key' of the mapped table, from the + which comprise the 'primary key' of the mapped table, from the perspective of this :class:`.Mapper`. This list is against the selectable in :attr:`~.Mapper.mapped_table`. In the @@ -293,7 +293,7 @@ class Mapper(object): referenced by the :class:`.Join`. The list is also not necessarily the same as the primary key column - collection associated with the underlying tables; the :class:`.Mapper` + collection associated with the underlying tables; the :class:`.Mapper` features a ``primary_key`` argument that can override what the :class:`.Mapper` considers as primary key columns. @@ -320,7 +320,7 @@ class Mapper(object): """ single = None - """Represent ``True`` if this :class:`.Mapper` is a single table + """Represent ``True`` if this :class:`.Mapper` is a single table inheritance mapper. :attr:`~.Mapper.local_table` will be ``None`` if this flag is set. @@ -331,8 +331,8 @@ class Mapper(object): """ non_primary = None - """Represent ``True`` if this :class:`.Mapper` is a "non-primary" - mapper, e.g. a mapper that is used only to selet rows but not for + """Represent ``True`` if this :class:`.Mapper` is a "non-primary" + mapper, e.g. a mapper that is used only to selet rows but not for persistence management. This is a *read only* attribute determined during mapper construction. @@ -356,10 +356,10 @@ class Mapper(object): """A mapping of "polymorphic identity" identifiers mapped to :class:`.Mapper` instances, within an inheritance scenario. - The identifiers can be of any type which is comparable to the + The identifiers can be of any type which is comparable to the type of column represented by :attr:`~.Mapper.polymorphic_on`. - An inheritance chain of mappers will all reference the same + An inheritance chain of mappers will all reference the same polymorphic map object. The object is used to correlate incoming result rows to target mappers. @@ -394,10 +394,10 @@ class Mapper(object): """ columns = None - """A collection of :class:`.Column` or other scalar expression + """A collection of :class:`.Column` or other scalar expression objects maintained by this :class:`.Mapper`. - The collection behaves the same as that of the ``c`` attribute on + The collection behaves the same as that of the ``c`` attribute on any :class:`.Table` object, except that only those columns included in this mapping are present, and are keyed based on the attribute name defined in the mapping, not necessarily the ``key`` attribute of the @@ -411,11 +411,11 @@ class Mapper(object): validators = None """An immutable dictionary of attributes which have been decorated - using the :func:`~.orm.validates` decorator. - + using the :func:`~.orm.validates` decorator. + The dictionary contains string attribute names as keys mapped to the actual validation method. - + """ c = None @@ -439,13 +439,13 @@ class Mapper(object): self.inherits = class_mapper(self.inherits, compile=False) if not issubclass(self.class_, self.inherits.class_): raise sa_exc.ArgumentError( - "Class '%s' does not inherit from '%s'" % + "Class '%s' does not inherit from '%s'" % (self.class_.__name__, self.inherits.class_.__name__)) if self.non_primary != self.inherits.non_primary: np = not self.non_primary and "primary" or "non-primary" raise sa_exc.ArgumentError( "Inheritance of %s mapper for class '%s' is " - "only allowed from a %s mapper" % + "only allowed from a %s mapper" % (np, self.class_.__name__, np)) # inherit_condition is optional. if self.local_table is None: @@ -468,7 +468,7 @@ class Mapper(object): self.inherits.local_table, self.local_table) self.mapped_table = sql.join( - self.inherits.mapped_table, + self.inherits.mapped_table, self.local_table, self.inherit_condition) @@ -495,7 +495,7 @@ class Mapper(object): "the inherited versioning column. " "version_id_col should only be specified on " "the base-most mapper that includes versioning." % - (self.version_id_col.description, + (self.version_id_col.description, self.inherits.version_id_col.description) ) @@ -524,7 +524,7 @@ class Mapper(object): if self.mapped_table is None: raise sa_exc.ArgumentError( - "Mapper '%s' does not have a mapped_table specified." + "Mapper '%s' does not have a mapped_table specified." % self) def _set_with_polymorphic(self, with_polymorphic): @@ -586,7 +586,7 @@ class Mapper(object): if self.inherits: self.dispatch._update(self.inherits.dispatch) super_extensions = set( - chain(*[m._deprecated_extensions + chain(*[m._deprecated_extensions for m in self.inherits.iterate_to_root()])) else: super_extensions = set() @@ -598,7 +598,7 @@ class Mapper(object): def _configure_listeners(self): if self.inherits: super_extensions = set( - chain(*[m._deprecated_extensions + chain(*[m._deprecated_extensions for m in self.inherits.iterate_to_root()])) else: super_extensions = set() @@ -645,8 +645,8 @@ class Mapper(object): "remove *all* current mappers from all classes." % self.class_) #else: - # a ClassManager may already exist as - # ClassManager.instrument_attribute() creates + # a ClassManager may already exist as + # ClassManager.instrument_attribute() creates # new managers for each subclass if they don't yet exist. _mapper_registry[self] = True @@ -660,8 +660,8 @@ class Mapper(object): manager.mapper = self manager.deferred_scalar_loader = self._load_scalar_attributes - - # The remaining members can be added by any mapper, + + # The remaining members can be added by any mapper, # e_name None or not. if manager.info.get(_INSTRUMENTOR, False): return @@ -745,10 +745,10 @@ class Mapper(object): self._readonly_props = set( self._columntoproperty[col] for col in self._columntoproperty - if not hasattr(col, 'table') or + if not hasattr(col, 'table') or col.table not in self._cols_by_table) - # if explicit PK argument sent, add those columns to the + # if explicit PK argument sent, add those columns to the # primary key mappings if self._primary_key_argument: for k in self._primary_key_argument: @@ -761,23 +761,23 @@ class Mapper(object): len(self._pks_by_table[self.mapped_table]) == 0: raise sa_exc.ArgumentError( "Mapper %s could not assemble any primary " - "key columns for mapped table '%s'" % + "key columns for mapped table '%s'" % (self, self.mapped_table.description)) elif self.local_table not in self._pks_by_table and \ isinstance(self.local_table, schema.Table): util.warn("Could not assemble any primary " "keys for locally mapped table '%s' - " - "no rows will be persisted in this Table." + "no rows will be persisted in this Table." % self.local_table.description) if self.inherits and \ not self.concrete and \ not self._primary_key_argument: - # if inheriting, the "primary key" for this mapper is + # if inheriting, the "primary key" for this mapper is # that of the inheriting (unless concrete or explicit) self.primary_key = self.inherits.primary_key else: - # determine primary key from argument or mapped_table pks - + # determine primary key from argument or mapped_table pks - # reduce to the minimal set of columns if self._primary_key_argument: primary_key = sql_util.reduce_columns( @@ -792,7 +792,7 @@ class Mapper(object): if len(primary_key) == 0: raise sa_exc.ArgumentError( "Mapper %s could not assemble any primary " - "key columns for mapped table '%s'" % + "key columns for mapped table '%s'" % (self, self.mapped_table.description)) self.primary_key = tuple(primary_key) @@ -844,19 +844,19 @@ class Mapper(object): if column in mapper._columntoproperty: column_key = mapper._columntoproperty[column].key - self._configure_property(column_key, - column, - init=False, + self._configure_property(column_key, + column, + init=False, setparent=True) def _configure_polymorphic_setter(self, init=False): - """Configure an attribute on the mapper representing the - 'polymorphic_on' column, if applicable, and not + """Configure an attribute on the mapper representing the + 'polymorphic_on' column, if applicable, and not already generated by _configure_properties (which is typical). Also create a setter function which will assign this attribute to the value of the 'polymorphic_identity' - upon instance construction, also if applicable. This + upon instance construction, also if applicable. This routine will run when an instance is created. """ @@ -905,15 +905,15 @@ class Mapper(object): else: # polymorphic_on is a Column or SQL expression and doesn't # appear to be mapped. - # this means it can be 1. only present in the with_polymorphic + # this means it can be 1. only present in the with_polymorphic # selectable or 2. a totally standalone SQL expression which we'd # hope is compatible with this mapper's mapped_table col = self.mapped_table.corresponding_column(self.polymorphic_on) if col is None: - # polymorphic_on doesn't derive from any column/expression + # polymorphic_on doesn't derive from any column/expression # isn't present in the mapped table. - # we will make a "hidden" ColumnProperty for it. - # Just check that if it's directly a schema.Column and we + # we will make a "hidden" ColumnProperty for it. + # Just check that if it's directly a schema.Column and we # have with_polymorphic, it's likely a user error if the # schema.Column isn't represented somehow in either mapped_table or # with_polymorphic. Otherwise as of 0.7.4 we just go with it @@ -931,11 +931,11 @@ class Mapper(object): "loads will not function properly" % col.description) else: - # column/expression that polymorphic_on derives from + # column/expression that polymorphic_on derives from # is present in our mapped table # and is probably mapped, but polymorphic_on itself - # is not. This happens when - # the polymorphic_on is only directly present in the + # is not. This happens when + # the polymorphic_on is only directly present in the # with_polymorphic selectable, as when use polymorphic_union. # we'll make a separate ColumnProperty for it. instrument = True @@ -951,7 +951,7 @@ class Mapper(object): key = col.key self._configure_property( - key, + key, properties.ColumnProperty(col, _instrument=instrument), init=init, setparent=True) polymorphic_key = key @@ -997,15 +997,15 @@ class Mapper(object): self._configure_property(key, prop, init=False, setparent=False) elif key not in self._props: self._configure_property( - key, - properties.ConcreteInheritedProperty(), + key, + properties.ConcreteInheritedProperty(), init=init, setparent=True) def _configure_property(self, key, prop, init=True, setparent=True): self._log("_configure_property(%s, %s)", key, prop.__class__.__name__) if not isinstance(prop, MapperProperty): - # we were passed a Column or a list of Columns; + # we were passed a Column or a list of Columns; # generate a properties.ColumnProperty columns = util.to_list(prop) column = columns[0] @@ -1025,7 +1025,7 @@ class Mapper(object): "explicitly." % (prop.columns[-1], column, key)) - # existing properties.ColumnProperty from an inheriting + # existing properties.ColumnProperty from an inheriting # mapper. make a copy and append our column to it prop = prop.copy() prop.columns.insert(0, column) @@ -1064,14 +1064,14 @@ class Mapper(object): "(including its availability as a foreign key), " "use the 'include_properties' or 'exclude_properties' " "mapper arguments to control specifically which table " - "columns get mapped." % + "columns get mapped." % (key, self, column.key, prop)) if isinstance(prop, properties.ColumnProperty): col = self.mapped_table.corresponding_column(prop.columns[0]) - # if the column is not present in the mapped table, - # test if a column has been added after the fact to the + # if the column is not present in the mapped table, + # test if a column has been added after the fact to the # parent table (or their parent, etc.) [ticket:1570] if col is None and self.inherits: path = [self] @@ -1085,20 +1085,20 @@ class Mapper(object): break path.append(m) - # subquery expression, column not present in the mapped + # subquery expression, column not present in the mapped # selectable. if col is None: col = prop.columns[0] - # column is coming in after _readonly_props was + # column is coming in after _readonly_props was # initialized; check for 'readonly' if hasattr(self, '_readonly_props') and \ - (not hasattr(col, 'table') or + (not hasattr(col, 'table') or col.table not in self._cols_by_table): self._readonly_props.add(prop) else: - # if column is coming in after _cols_by_table was + # if column is coming in after _cols_by_table was # initialized, ensure the col is in the right set if hasattr(self, '_cols_by_table') and \ col.table in self._cols_by_table and \ @@ -1198,10 +1198,10 @@ class Mapper(object): def _log_desc(self): return "(" + self.class_.__name__ + \ "|" + \ - (self.local_table is not None and - self.local_table.description or + (self.local_table is not None and + self.local_table.description or str(self.local_table)) +\ - (self.non_primary and + (self.non_primary and "|non-primary" or "") + ")" def _log(self, msg, *args): @@ -1222,7 +1222,7 @@ class Mapper(object): def __str__(self): return "Mapper|%s|%s%s" % ( self.class_.__name__, - self.local_table is not None and + self.local_table is not None and self.local_table.description or None, self.non_primary and "|non-primary" or "" ) @@ -1281,7 +1281,7 @@ class Mapper(object): for m in mappers: if not m.isa(self): raise sa_exc.InvalidRequestError( - "%r does not inherit from %r" % + "%r does not inherit from %r" % (m, self)) else: mappers = [] @@ -1347,7 +1347,7 @@ class Mapper(object): self._mappers_from_spec(spec, selectable), False) - def _with_polymorphic_args(self, spec=None, selectable=False, + def _with_polymorphic_args(self, spec=None, selectable=False, innerjoin=False): if self.with_polymorphic: if not spec: @@ -1360,7 +1360,7 @@ class Mapper(object): if selectable is not None: return mappers, selectable else: - return mappers, self._selectable_from_mappers(mappers, + return mappers, self._selectable_from_mappers(mappers, innerjoin) @_memoized_configured_property @@ -1387,7 +1387,7 @@ class Mapper(object): mappers]) ): if getattr(c, '_is_polymorphic_discriminator', False) and \ - (self.polymorphic_on is None or + (self.polymorphic_on is None or c.columns[0] is not self.polymorphic_on): continue yield c @@ -1475,7 +1475,7 @@ class Mapper(object): return result def _is_userland_descriptor(self, obj): - if isinstance(obj, (MapperProperty, + if isinstance(obj, (MapperProperty, attributes.QueryableAttribute)): return False elif not hasattr(obj, '__get__'): @@ -1528,7 +1528,7 @@ class Mapper(object): return False def common_parent(self, other): - """Return true if the given mapper shares a + """Return true if the given mapper shares a common inherited parent as this mapper.""" return self.base_mapper is other.base_mapper @@ -1657,7 +1657,7 @@ class Mapper(object): for col in self.primary_key ] - def _get_state_attr_by_column(self, state, dict_, column, + def _get_state_attr_by_column(self, state, dict_, column, passive=attributes.PASSIVE_OFF): prop = self._columntoproperty[column] return state.manager[prop.key].impl.get(state, dict_, passive=passive) @@ -1671,7 +1671,7 @@ class Mapper(object): dict_ = attributes.instance_dict(obj) return self._get_committed_state_attr_by_column(state, dict_, column) - def _get_committed_state_attr_by_column(self, state, dict_, + def _get_committed_state_attr_by_column(self, state, dict_, column, passive=attributes.PASSIVE_OFF): prop = self._columntoproperty[column] @@ -1698,8 +1698,8 @@ class Mapper(object): if statement is not None: result = loading.load_on_ident( session.query(self).from_statement(statement), - None, - only_load_props=attribute_names, + None, + only_load_props=attribute_names, refresh_state=state ) @@ -1723,17 +1723,17 @@ class Mapper(object): _none_set.issuperset(identity_key): util.warn("Instance %s to be refreshed doesn't " "contain a full primary key - can't be refreshed " - "(and shouldn't be expired, either)." + "(and shouldn't be expired, either)." % state_str(state)) return result = loading.load_on_ident( session.query(self), - identity_key, - refresh_state=state, + identity_key, + refresh_state=state, only_load_props=attribute_names) - # if instance is pending, a refresh operation + # if instance is pending, a refresh operation # may not complete (even if PK attributes are assigned) if has_key and result is None: raise orm_exc.ObjectDeletedError(state) @@ -1742,16 +1742,16 @@ class Mapper(object): """assemble a WHERE clause which retrieves a given state by primary key, using a minimized set of tables. - Applies to a joined-table inheritance mapper where the + Applies to a joined-table inheritance mapper where the requested attribute names are only present on joined tables, - not the base table. The WHERE clause attempts to include + not the base table. The WHERE clause attempts to include only those tables to minimize joins. """ props = self._props tables = set(chain( - *[sql_util.find_tables(c, check_columns=True) + *[sql_util.find_tables(c, check_columns=True) for key in attribute_names for c in props[key].columns] )) @@ -1770,8 +1770,8 @@ class Mapper(object): if leftcol.table not in tables: leftval = self._get_committed_state_attr_by_column( - state, state.dict, - leftcol, + state, state.dict, + leftcol, passive=attributes.PASSIVE_NO_INITIALIZE) if leftval is attributes.PASSIVE_NO_RESULT or leftval is None: raise ColumnsNotAvailable() @@ -1779,8 +1779,8 @@ class Mapper(object): type_=binary.right.type) elif rightcol.table not in tables: rightval = self._get_committed_state_attr_by_column( - state, state.dict, - rightcol, + state, state.dict, + rightcol, passive=attributes.PASSIVE_NO_INITIALIZE) if rightval is attributes.PASSIVE_NO_RESULT or rightval is None: raise ColumnsNotAvailable() @@ -1796,8 +1796,8 @@ class Mapper(object): start = True if start and not mapper.single: allconds.append(visitors.cloned_traverse( - mapper.inherit_condition, - {}, + mapper.inherit_condition, + {}, {'binary':visit_binary} ) ) @@ -1830,7 +1830,7 @@ class Mapper(object): visited_states = set() prp, mpp = object(), object() - visitables = deque([(deque(self._props.values()), prp, + visitables = deque([(deque(self._props.values()), prp, state, state.dict)]) while visitables: @@ -1843,7 +1843,7 @@ class Mapper(object): prop = iterator.popleft() if type_ not in prop.cascade: continue - queue = deque(prop.cascade_iterator(type_, parent_state, + queue = deque(prop.cascade_iterator(type_, parent_state, parent_dict, visited_states, halt_on)) if queue: visitables.append((queue,mpp, None, None)) @@ -1852,8 +1852,8 @@ class Mapper(object): corresponding_dict = iterator.popleft() yield instance, instance_mapper, \ corresponding_state, corresponding_dict - visitables.append((deque(instance_mapper._props.values()), - prp, corresponding_state, + visitables.append((deque(instance_mapper._props.values()), + prp, corresponding_state, corresponding_dict)) @_memoized_configured_property @@ -1865,9 +1865,31 @@ class Mapper(object): table_to_mapper = {} for mapper in self.base_mapper.self_and_descendants: for t in mapper.tables: - table_to_mapper[t] = mapper + table_to_mapper.setdefault(t, mapper) + + def skip(fk): + # attempt to skip dependencies that are not + # significant to the inheritance chain + # for two tables that are related by inheritance. + # while that dependency may be important, it's techinically + # not what we mean to sort on here. + parent = table_to_mapper.get(fk.parent.table) + dep = table_to_mapper.get(fk.column.table) + if parent is not None and \ + dep is not None and \ + dep is not parent and \ + dep.inherit_condition is not None: + cols = set(sql_util.find_columns(dep.inherit_condition)) + if parent.inherit_condition is not None: + cols = cols.union(sql_util.find_columns( + parent.inherit_condition)) + return fk.parent not in cols and fk.column not in cols + else: + return fk.parent not in cols + return False - sorted_ = sql_util.sort_tables(table_to_mapper.iterkeys()) + sorted_ = sql_util.sort_tables(table_to_mapper.iterkeys(), + skip_fn=skip) ret = util.OrderedDict() for t in sorted_: ret[t] = table_to_mapper[t] @@ -1910,7 +1932,7 @@ class Mapper(object): @util.memoized_property def _table_to_equated(self): - """memoized map of tables to collections of columns to be + """memoized map of tables to collections of columns to be synchronized upwards to the base mapper.""" result = util.defaultdict(list) @@ -1931,14 +1953,14 @@ def configure_mappers(): """Initialize the inter-mapper relationships of all mappers that have been constructed thus far. - This function can be called any number of times, but in + This function can be called any number of times, but in most cases is handled internally. """ global _new_mappers if not _new_mappers: - return + return _call_configured = None _COMPILE_MUTEX.acquire() @@ -1954,8 +1976,8 @@ def configure_mappers(): return # initialize properties on all mappers - # note that _mapper_registry is unordered, which - # may randomly conceal/reveal issues related to + # note that _mapper_registry is unordered, which + # may randomly conceal/reveal issues related to # the order of mapper compilation for mapper in list(_mapper_registry): if getattr(mapper, '_configure_failed', False): @@ -2022,7 +2044,7 @@ def validates(*names, **kw): condition which is not supported. :param \*names: list of attribute names to be validated. - :param include_removes: if True, "remove" events will be + :param include_removes: if True, "remove" events will be sent as well - the validation function must accept an additional argument "is_remove" which will be a boolean. @@ -2043,7 +2065,7 @@ def _event_on_load(state, ctx): def _event_on_first_init(manager, cls): """Initial mapper compilation trigger. - + instrumentation calls this one when InstanceState is first generated, and is needed for legacy mutable attributes to work. @@ -2056,11 +2078,11 @@ def _event_on_first_init(manager, cls): def _event_on_init(state, args, kwargs): """Run init_instance hooks. - + This also includes mapper compilation, normally not needed here but helps with some piecemeal configuration scenarios (such as in the ORM tutorial). - + """ instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py index 83a75a77f5..b0818b8910 100644 --- a/lib/sqlalchemy/sql/util.py +++ b/lib/sqlalchemy/sql/util.py @@ -12,7 +12,7 @@ from collections import deque """Utility functions that build upon SQL and Schema constructs.""" -def sort_tables(tables): +def sort_tables(tables, skip_fn=None): """sort a collection of Table objects in order of their foreign-key dependency.""" tables = list(tables) @@ -20,6 +20,8 @@ def sort_tables(tables): def visit_foreign_key(fkey): if fkey.use_alter: return + elif skip_fn and skip_fn(fkey): + return parent_table = fkey.column.table if parent_table in tables: child_table = fkey.parent.table @@ -27,8 +29,8 @@ def sort_tables(tables): tuples.append((parent_table, child_table)) for table in tables: - visitors.traverse(table, - {'schema_visitor':True}, + visitors.traverse(table, + {'schema_visitor':True}, {'foreign_key':visit_foreign_key}) tuples.extend( @@ -38,9 +40,9 @@ def sort_tables(tables): return list(topological.sort(tuples, tables)) def find_join_source(clauses, join_to): - """Given a list of FROM clauses and a selectable, - return the first index and element from the list of - clauses which can be joined against the selectable. returns + """Given a list of FROM clauses and a selectable, + return the first index and element from the list of + clauses which can be joined against the selectable. returns None, None if no match is found. e.g.:: @@ -66,25 +68,25 @@ def find_join_source(clauses, join_to): def visit_binary_product(fn, expr): """Produce a traversal of the given expression, delivering column comparisons to the given function. - + The function is of the form:: - + def my_fn(binary, left, right) - - For each binary expression located which has a + + For each binary expression located which has a comparison operator, the product of "left" and "right" will be delivered to that function, in terms of that binary. - + Hence an expression like:: - + and_( (a + b) == q + func.sum(e + f), j == r ) - + would have the traversal:: - + a q a e a f @@ -96,7 +98,7 @@ def visit_binary_product(fn, expr): That is, every combination of "left" and "right" that doesn't further contain a binary comparison is passed as pairs. - + """ stack = [] def visit(element): @@ -121,8 +123,8 @@ def visit_binary_product(fn, expr): yield e list(visit(expr)) -def find_tables(clause, check_columns=False, - include_aliases=False, include_joins=False, +def find_tables(clause, check_columns=False, + include_aliases=False, include_joins=False, include_selects=False, include_crud=False): """locate Table objects within the given expression.""" @@ -171,7 +173,7 @@ def unwrap_order_by(clause): ( not isinstance(t, expression._UnaryExpression) or \ not operators.is_ordering_modifier(t.modifier) - ): + ): cols.add(t) else: for c in t.get_children(): @@ -226,7 +228,7 @@ def _quote_ddl_expr(element): class _repr_params(object): """A string view of bound parameters, truncating display to the given number of 'multi' parameter sets. - + """ def __init__(self, params, batches): self.params = params @@ -246,7 +248,7 @@ class _repr_params(object): def expression_as_ddl(clause): - """Given a SQL expression, convert for usage in DDL, such as + """Given a SQL expression, convert for usage in DDL, such as CREATE INDEX and CHECK CONSTRAINT. Converts bind params into quoted literals, column identifiers @@ -285,7 +287,7 @@ def adapt_criterion_to_null(crit, nulls): return visitors.cloned_traverse(crit, {}, {'binary':visit_binary}) -def join_condition(a, b, ignore_nonexistent_tables=False, +def join_condition(a, b, ignore_nonexistent_tables=False, a_subset=None, consider_as_foreign_keys=None): """create a join condition between two tables or selectables. @@ -321,7 +323,7 @@ def join_condition(a, b, ignore_nonexistent_tables=False, if left is None: continue for fk in sorted( - b.foreign_keys, + b.foreign_keys, key=lambda fk:fk.parent._creation_order): if consider_as_foreign_keys is not None and \ fk.parent not in consider_as_foreign_keys: @@ -339,7 +341,7 @@ def join_condition(a, b, ignore_nonexistent_tables=False, constraints.add(fk.constraint) if left is not b: for fk in sorted( - left.foreign_keys, + left.foreign_keys, key=lambda fk:fk.parent._creation_order): if consider_as_foreign_keys is not None and \ fk.parent not in consider_as_foreign_keys: @@ -385,12 +387,12 @@ def join_condition(a, b, ignore_nonexistent_tables=False, class Annotated(object): """clones a ClauseElement and applies an 'annotations' dictionary. - Unlike regular clones, this clone also mimics __hash__() and + Unlike regular clones, this clone also mimics __hash__() and __cmp__() of the original element so that it takes its place in hashed collections. A reference to the original element is maintained, for the important - reason of keeping its hash value current. When GC'ed, the + reason of keeping its hash value current. When GC'ed, the hash value may be reused, causing conflicts. """ @@ -406,13 +408,13 @@ class Annotated(object): try: cls = annotated_classes[element.__class__] except KeyError: - cls = annotated_classes[element.__class__] = type.__new__(type, - "Annotated%s" % element.__class__.__name__, + cls = annotated_classes[element.__class__] = type.__new__(type, + "Annotated%s" % element.__class__.__name__, (Annotated, element.__class__), {}) return object.__new__(cls) def __init__(self, element, values): - # force FromClause to generate their internal + # force FromClause to generate their internal # collections into __dict__ if isinstance(element, expression.FromClause): element.c @@ -481,7 +483,7 @@ for cls in expression.__dict__.values() + [schema.Column, schema.Table]: exec "annotated_classes[cls] = Annotated%s" % (cls.__name__) def _deep_annotate(element, annotations, exclude=None): - """Deep copy the given ClauseElement, annotating each element + """Deep copy the given ClauseElement, annotating each element with the given annotations dictionary. Elements within the exclude collection will be cloned but not annotated. @@ -528,17 +530,17 @@ def _deep_deannotate(element, values=None): element = clone(element) return element -def _shallow_annotate(element, annotations): - """Annotate the given ClauseElement and copy its internals so that - internal objects refer to the new annotated object. +def _shallow_annotate(element, annotations): + """Annotate the given ClauseElement and copy its internals so that + internal objects refer to the new annotated object. - Basically used to apply a "dont traverse" annotation to a - selectable, without digging throughout the whole - structure wasting time. - """ - element = element._annotate(annotations) - element._copy_internals() - return element + Basically used to apply a "dont traverse" annotation to a + selectable, without digging throughout the whole + structure wasting time. + """ + element = element._annotate(annotations) + element._copy_internals() + return element def splice_joins(left, right, stop_on=None): if left is None: @@ -626,7 +628,7 @@ def reduce_columns(columns, *clauses, **kw): return expression.ColumnSet(columns.difference(omit)) -def criterion_as_pairs(expression, consider_as_foreign_keys=None, +def criterion_as_pairs(expression, consider_as_foreign_keys=None, consider_as_referenced_keys=None, any_operator=False): """traverse an expression and locate binary criterion pairs.""" @@ -648,20 +650,20 @@ def criterion_as_pairs(expression, consider_as_foreign_keys=None, if consider_as_foreign_keys: if binary.left in consider_as_foreign_keys and \ - (col_is(binary.right, binary.left) or + (col_is(binary.right, binary.left) or binary.right not in consider_as_foreign_keys): pairs.append((binary.right, binary.left)) elif binary.right in consider_as_foreign_keys and \ - (col_is(binary.left, binary.right) or + (col_is(binary.left, binary.right) or binary.left not in consider_as_foreign_keys): pairs.append((binary.left, binary.right)) elif consider_as_referenced_keys: if binary.left in consider_as_referenced_keys and \ - (col_is(binary.right, binary.left) or + (col_is(binary.right, binary.left) or binary.right not in consider_as_referenced_keys): pairs.append((binary.left, binary.right)) elif binary.right in consider_as_referenced_keys and \ - (col_is(binary.left, binary.right) or + (col_is(binary.left, binary.right) or binary.left not in consider_as_referenced_keys): pairs.append((binary.right, binary.left)) else: @@ -678,17 +680,17 @@ def criterion_as_pairs(expression, consider_as_foreign_keys=None, def folded_equivalents(join, equivs=None): """Return a list of uniquely named columns. - The column list of the given Join will be narrowed + The column list of the given Join will be narrowed down to a list of all equivalently-named, equated columns folded into one column, where 'equated' means they are equated to each other in the ON clause of this join. This function is used by Join.select(fold_equivalents=True). - Deprecated. This function is used for a certain kind of + Deprecated. This function is used for a certain kind of "polymorphic_union" which is designed to achieve joined table inheritance where the base table has no "discriminator" - column; [ticket:1131] will provide a better way to + column; [ticket:1131] will provide a better way to achieve this. """ @@ -773,9 +775,9 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor): s.c.col1 == table2.c.col1 """ - def __init__(self, selectable, equivalents=None, - include=None, exclude=None, - include_fn=None, exclude_fn=None, + def __init__(self, selectable, equivalents=None, + include=None, exclude=None, + include_fn=None, exclude_fn=None, adapt_on_names=False): self.__traverse_options__ = {'stop_on':[selectable]} self.selectable = selectable @@ -794,12 +796,12 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor): def _corresponding_column(self, col, require_embedded, _seen=util.EMPTY_SET): newcol = self.selectable.corresponding_column( - col, + col, require_embedded=require_embedded) if newcol is None and col in self.equivalents and col not in _seen: for equiv in self.equivalents[col]: - newcol = self._corresponding_column(equiv, - require_embedded=require_embedded, + newcol = self._corresponding_column(equiv, + require_embedded=require_embedded, _seen=_seen.union([col])) if newcol is not None: return newcol @@ -823,14 +825,14 @@ class ClauseAdapter(visitors.ReplacingCloningVisitor): class ColumnAdapter(ClauseAdapter): """Extends ClauseAdapter with extra utility functions. - Provides the ability to "wrap" this ClauseAdapter + Provides the ability to "wrap" this ClauseAdapter around another, a columns dictionary which returns - adapted elements given an original, and an + adapted elements given an original, and an adapted_row() factory. """ - def __init__(self, selectable, equivalents=None, - chain_to=None, include=None, + def __init__(self, selectable, equivalents=None, + chain_to=None, include=None, exclude=None, adapt_required=False): ClauseAdapter.__init__(self, selectable, equivalents, include, exclude) if chain_to: @@ -866,7 +868,7 @@ class ColumnAdapter(ClauseAdapter): c = c.label(None) # adapt_required indicates that if we got the same column - # back which we put in (i.e. it passed through), + # back which we put in (i.e. it passed through), # it's not correct. this is used by eagerloading which # knows that all columns and expressions need to be adapted # to a result row, and a "passthrough" is definitely targeting diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 271dd49597..0e647a66e3 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -10,6 +10,7 @@ from test.lib import testing, engines from test.lib import fixtures from test.orm import _fixtures from test.lib.schema import Table, Column +from sqlalchemy.ext.declarative import declarative_base class O2MTest(fixtures.MappedTest): """deals with inheritance and one-to-many relationships""" @@ -75,15 +76,15 @@ class O2MTest(fixtures.MappedTest): class PolymorphicOnNotLocalTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): - t1 = Table('t1', metadata, + t1 = Table('t1', metadata, Column('id', Integer, primary_key=True, - test_needs_autoincrement=True), + test_needs_autoincrement=True), Column('x', String(10)), Column('q', String(10))) - t2 = Table('t2', metadata, + t2 = Table('t2', metadata, Column('id', Integer, primary_key=True, - test_needs_autoincrement=True), - Column('y', String(10)), + test_needs_autoincrement=True), + Column('y', String(10)), Column('xid', ForeignKey('t1.id'))) @classmethod @@ -181,7 +182,7 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): "discriminator":column_property(expr) }, polymorphic_identity="parent", polymorphic_on=expr) - mapper(Child, t2, inherits=Parent, + mapper(Child, t2, inherits=Parent, polymorphic_identity="child") self._roundtrip(parent_ident='p', child_ident='c') @@ -213,7 +214,7 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): self._roundtrip(parent_ident='p', child_ident='c') def test_polymorphic_on_expr_implicit_map_no_label_single(self): - """test that single_table_criterion is propagated + """test that single_table_criterion is propagated with a standalone expr""" t2, t1 = self.tables.t2, self.tables.t1 Parent, Child = self.classes.Parent, self.classes.Child @@ -228,7 +229,7 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): self._roundtrip(parent_ident='p', child_ident='c') def test_polymorphic_on_expr_implicit_map_w_label_single(self): - """test that single_table_criterion is propagated + """test that single_table_criterion is propagated with a standalone expr""" t2, t1 = self.tables.t2, self.tables.t1 Parent, Child = self.classes.Parent, self.classes.Child @@ -254,7 +255,7 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): "discriminator":cprop }, polymorphic_identity="parent", polymorphic_on=cprop) - mapper(Child, t2, inherits=Parent, + mapper(Child, t2, inherits=Parent, polymorphic_identity="child") self._roundtrip(parent_ident='p', child_ident='c') @@ -271,7 +272,7 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): "discriminator":cprop }, polymorphic_identity="parent", polymorphic_on="discriminator") - mapper(Child, t2, inherits=Parent, + mapper(Child, t2, inherits=Parent, polymorphic_identity="child") self._roundtrip(parent_ident='p', child_ident='c') @@ -323,13 +324,49 @@ class PolymorphicOnNotLocalTest(fixtures.MappedTest): [Child] ) +class SortOnlyOnImportantFKsTest(fixtures.MappedTest): + @classmethod + def define_tables(cls, metadata): + Table('a', metadata, + Column('id', Integer, primary_key=True), + Column('b_id', Integer, + ForeignKey('b.id', use_alter=True, name='b')) + ) + Table('b', metadata, + Column('id', Integer, ForeignKey('a.id'), primary_key=True) + ) + + @classmethod + def setup_classes(cls): + Base = declarative_base() + + class A(Base): + __tablename__ = "a" + + id = Column(Integer, primary_key=True) + b_id = Column(Integer, ForeignKey('b.id')) + + class B(A): + __tablename__ = "b" + + id = Column(Integer, ForeignKey('a.id'), primary_key=True) + + __mapper_args__ = {'inherit_condition': id == A.id} + + cls.classes.A = A + cls.classes.B = B + + def test_flush(self): + s = Session(testing.db) + s.add(self.classes.B()) + s.flush() class FalseDiscriminatorTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): global t1 - t1 = Table('t1', metadata, - Column('id', Integer, primary_key=True, test_needs_autoincrement=True), + t1 = Table('t1', metadata, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('type', Boolean, nullable=False)) def test_false_on_sub(self): @@ -405,12 +442,12 @@ class PolymorphicAttributeManagementTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): Table('table_a', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('class_name', String(50)) ) Table('table_b', metadata, - Column('id', Integer, ForeignKey('table_a.id'), + Column('id', Integer, ForeignKey('table_a.id'), primary_key=True), Column('class_name', String(50)) ) @@ -432,13 +469,13 @@ class PolymorphicAttributeManagementTest(fixtures.MappedTest): class C(B): pass - mapper(A, table_a, - polymorphic_on=table_a.c.class_name, + mapper(A, table_a, + polymorphic_on=table_a.c.class_name, polymorphic_identity='a') - mapper(B, table_b, inherits=A, - polymorphic_on=table_b.c.class_name, + mapper(B, table_b, inherits=A, + polymorphic_on=table_b.c.class_name, polymorphic_identity='b') - mapper(C, table_c, inherits=B, + mapper(C, table_c, inherits=B, polymorphic_identity='c') def test_poly_configured_immediate(self): @@ -489,25 +526,25 @@ class CascadeTest(fixtures.MappedTest): def define_tables(cls, metadata): global t1, t2, t3, t4 t1= Table('t1', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('data', String(30)) ) t2 = Table('t2', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('t1id', Integer, ForeignKey('t1.id')), Column('type', String(30)), Column('data', String(30)) ) t3 = Table('t3', metadata, - Column('id', Integer, ForeignKey('t2.id'), + Column('id', Integer, ForeignKey('t2.id'), primary_key=True), Column('moredata', String(30))) t4 = Table('t4', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('t3id', Integer, ForeignKey('t3.id')), Column('data', String(30))) @@ -757,7 +794,7 @@ class EagerLazyTest(fixtures.MappedTest): self.assert_(len(q.first().eager) == 1) class EagerTargetingTest(fixtures.MappedTest): - """test a scenario where joined table inheritance might be + """test a scenario where joined table inheritance might be confused as an eagerly loaded joined table.""" @classmethod @@ -782,7 +819,7 @@ class EagerTargetingTest(fixtures.MappedTest): class B(A): pass - mapper(A, a_table, polymorphic_on=a_table.c.type, polymorphic_identity='A', + mapper(A, a_table, polymorphic_on=a_table.c.type, polymorphic_identity='A', properties={ 'children': relationship(A, order_by=a_table.c.name) }) @@ -945,9 +982,9 @@ class VersioningTest(fixtures.MappedTest): class Stuff(Base): pass mapper(Stuff, stuff) - mapper(Base, base, - polymorphic_on=base.c.discriminator, - version_id_col=base.c.version_id, + mapper(Base, base, + polymorphic_on=base.c.discriminator, + version_id_col=base.c.version_id, polymorphic_identity=1, properties={ 'stuff':relationship(Stuff) }) @@ -971,7 +1008,7 @@ class VersioningTest(fixtures.MappedTest): sess.flush() assert_raises(orm_exc.StaleDataError, - sess2.query(Base).with_lockmode('read').get, + sess2.query(Base).with_lockmode('read').get, s1.id) if not testing.db.dialect.supports_sane_rowcount: @@ -994,8 +1031,8 @@ class VersioningTest(fixtures.MappedTest): class Sub(Base): pass - mapper(Base, base, - polymorphic_on=base.c.discriminator, + mapper(Base, base, + polymorphic_on=base.c.discriminator, version_id_col=base.c.version_id, polymorphic_identity=1) mapper(Sub, subtable, inherits=Base, polymorphic_identity=2) @@ -1073,16 +1110,16 @@ class DistinctPKTest(fixtures.MappedTest): def test_explicit_props(self): person_mapper = mapper(Person, person_table) mapper(Employee, employee_table, inherits=person_mapper, - properties={'pid':person_table.c.id, + properties={'pid':person_table.c.id, 'eid':employee_table.c.id}) self._do_test(False) def test_explicit_composite_pk(self): person_mapper = mapper(Person, person_table) - mapper(Employee, employee_table, - inherits=person_mapper, + mapper(Employee, employee_table, + inherits=person_mapper, primary_key=[person_table.c.id, employee_table.c.id]) - assert_raises_message(sa_exc.SAWarning, + assert_raises_message(sa_exc.SAWarning, r"On mapper Mapper\|Employee\|employees, " "primary key column 'persons.id' is being " "combined with distinct primary key column 'employees.id' " @@ -1188,7 +1225,7 @@ class OverrideColKeyTest(fixtures.MappedTest): def define_tables(cls, metadata): global base, subtable - base = Table('base', metadata, + base = Table('base', metadata, Column('base_id', Integer, primary_key=True, test_needs_autoincrement=True), Column('data', String(255)), Column('sqlite_fixer', String(10)) @@ -1239,7 +1276,7 @@ class OverrideColKeyTest(fixtures.MappedTest): class_mapper(Sub).get_property('id').columns, [base.c.base_id, subtable.c.base_id] ) - + s1 = Sub() s1.id = 10 sess = create_session() @@ -1412,7 +1449,7 @@ class OptimizedLoadTest(fixtures.MappedTest): Column('type', String(50)), Column('counter', Integer, server_default="1") ) - Table('sub', metadata, + Table('sub', metadata, Column('id', Integer, ForeignKey('base.id'), primary_key=True), Column('sub', String(50)), Column('counter', Integer, server_default="1"), @@ -1429,7 +1466,7 @@ class OptimizedLoadTest(fixtures.MappedTest): ) def test_optimized_passes(self): - """"test that the 'optimized load' routine doesn't crash when + """"test that the 'optimized load' routine doesn't crash when a column in the join condition is not available.""" base, sub = self.tables.base, self.tables.sub @@ -1444,7 +1481,7 @@ class OptimizedLoadTest(fixtures.MappedTest): # redefine Sub's "id" to favor the "id" col in the subtable. # "id" is also part of the primary join condition - mapper(Sub, sub, inherits=Base, + mapper(Sub, sub, inherits=Base, polymorphic_identity='sub', properties={'id':[sub.c.id, base.c.id]}) sess = sessionmaker()() @@ -1453,8 +1490,8 @@ class OptimizedLoadTest(fixtures.MappedTest): sess.commit() sess.expunge_all() - # load s1 via Base. s1.id won't populate since it's relative to - # the "sub" table. The optimized load kicks in and tries to + # load s1 via Base. s1.id won't populate since it's relative to + # the "sub" table. The optimized load kicks in and tries to # generate on the primary join, but cannot since "id" is itself unloaded. # the optimized load needs to return "None" so regular full-row loading proceeds s1 = sess.query(Base).first() @@ -1499,7 +1536,7 @@ class OptimizedLoadTest(fixtures.MappedTest): sess.expunge_all() # query a bunch of rows to ensure there's no cartesian # product against "base" occurring, it is in fact - # detecting that "base" needs to be in the join + # detecting that "base" needs to be in the join # criterion eq_( sess.query(Base).order_by(Base.id).all(), @@ -1585,24 +1622,24 @@ class OptimizedLoadTest(fixtures.MappedTest): pass class Sub(Base): pass - mapper(Base, base, polymorphic_on=base.c.type, + mapper(Base, base, polymorphic_on=base.c.type, polymorphic_identity='base') m = mapper(Sub, sub, inherits=Base, polymorphic_identity='sub') s1 = Sub() - assert m._optimized_get_statement(attributes.instance_state(s1), + assert m._optimized_get_statement(attributes.instance_state(s1), ['counter2']) is None # loads s1.id as None eq_(s1.id, None) # this now will come up with a value of None for id - should reject - assert m._optimized_get_statement(attributes.instance_state(s1), + assert m._optimized_get_statement(attributes.instance_state(s1), ['counter2']) is None s1.id = 1 attributes.instance_state(s1).commit_all(s1.__dict__, None) - assert m._optimized_get_statement(attributes.instance_state(s1), + assert m._optimized_get_statement(attributes.instance_state(s1), ['counter2']) is not None def test_load_expired_on_pending_twolevel(self): @@ -1617,7 +1654,7 @@ class OptimizedLoadTest(fixtures.MappedTest): class SubSub(Sub): pass - mapper(Base, base, polymorphic_on=base.c.type, + mapper(Base, base, polymorphic_on=base.c.type, polymorphic_identity='base') mapper(Sub, sub, inherits=Base, polymorphic_identity='sub') mapper(SubSub, subsub, inherits=Sub, polymorphic_identity='subsub') @@ -1720,7 +1757,7 @@ class InhCondTest(fixtures.TestBase): mapper(Base, base_table) # succeeds, despite "owner" table not configured yet - m2 = mapper(Derived, derived_table, + m2 = mapper(Derived, derived_table, inherits=Base) assert m2.inherit_condition.compare( base_table.c.id==derived_table.c.id @@ -1732,7 +1769,7 @@ class InhCondTest(fixtures.TestBase): Column("id", Integer, primary_key=True) ) derived_table = Table("derived", m, - Column("id", Integer, ForeignKey('base.id'), + Column("id", Integer, ForeignKey('base.id'), primary_key=True), Column('order_id', Integer, ForeignKey('order.foo')) ) @@ -1782,7 +1819,7 @@ class InhCondTest(fixtures.TestBase): Column("id", Integer, primary_key=True) ) derived_table = Table("derived", m2, - Column("id", Integer, ForeignKey('base.id'), + Column("id", Integer, ForeignKey('base.id'), primary_key=True), ) @@ -1813,7 +1850,7 @@ class InhCondTest(fixtures.TestBase): Column("id", Integer, primary_key=True) ) derived_table = Table("derived", m, - Column("id", Integer, ForeignKey('base.q'), + Column("id", Integer, ForeignKey('base.q'), primary_key=True), ) @@ -1838,12 +1875,12 @@ class PKDiscriminatorTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): parents = Table('parents', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('name', String(60))) children = Table('children', metadata, - Column('id', Integer, ForeignKey('parents.id'), + Column('id', Integer, ForeignKey('parents.id'), primary_key=True), Column('type', Integer,primary_key=True), Column('name', String(60))) @@ -1892,7 +1929,7 @@ class NoPolyIdentInMiddleTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): Table('base', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('type', String(50), nullable=False), ) @@ -1969,7 +2006,7 @@ class DeleteOrphanTest(fixtures.MappedTest): """Test the fairly obvious, that an error is raised when attempting to insert an orphan. - Previous SQLA versions would check this constraint + Previous SQLA versions would check this constraint in memory which is the original rationale for this test. """ @@ -2014,14 +2051,14 @@ class PolymorphicUnionTest(fixtures.TestBase, testing.AssertsCompiledSQL): __dialect__ = 'default' def _fixture(self): - t1 = table('t1', column('c1', Integer), - column('c2', Integer), + t1 = table('t1', column('c1', Integer), + column('c2', Integer), column('c3', Integer)) - t2 = table('t2', column('c1', Integer), column('c2', Integer), - column('c3', Integer), + t2 = table('t2', column('c1', Integer), column('c2', Integer), + column('c3', Integer), column('c4', Integer)) - t3 = table('t3', column('c1', Integer), - column('c3', Integer), + t3 = table('t3', column('c1', Integer), + column('c3', Integer), column('c5', Integer)) return t1, t2, t3 @@ -2072,12 +2109,12 @@ class NameConflictTest(fixtures.MappedTest): @classmethod def define_tables(cls, metadata): content = Table('content', metadata, - Column('id', Integer, primary_key=True, + Column('id', Integer, primary_key=True, test_needs_autoincrement=True), Column('type', String(30)) ) foo = Table('foo', metadata, - Column('id', Integer, ForeignKey('content.id'), + Column('id', Integer, ForeignKey('content.id'), primary_key=True), Column('content_type', String(30)) ) @@ -2087,9 +2124,9 @@ class NameConflictTest(fixtures.MappedTest): pass class Foo(Content): pass - mapper(Content, self.tables.content, + mapper(Content, self.tables.content, polymorphic_on=self.tables.content.c.type) - mapper(Foo, self.tables.foo, inherits=Content, + mapper(Foo, self.tables.foo, inherits=Content, polymorphic_identity='foo') sess = create_session() f = Foo()