on dialect, but only works on Postgresql so far.
Courtesy Manlio Perillo, [ticket:1679]
+ - [feature] The "extend_existing" flag on Table
+ now allows for the reflection process to take
+ effect for a Table object that's already been
+ defined; when autoload=True and extend_existing=True
+ are both set, the full set of columns will be
+ reflected from the Table which will then
+ *overwrite* those columns already present,
+ rather than no activity occurring. Columns that
+ are present directly in the autoload run
+ will be used as always, however.
+ [ticket:1410]
+
- [bug] Fixed bug whereby TypeDecorator would
return a stale value for _type_affinity, when
using a TypeDecorator that "switches" types,
or Connection instance to be used for the table reflection. If
``None``, the underlying MetaData's bound connectable will be used.
- :param extend_existing: When ``True``, indicates that if this Table is already
+ :param extend_existing: When ``True``, indicates that if this :class:`.Table` is already
present in the given :class:`.MetaData`, apply further arguments within
the constructor to the existing :class:`.Table`.
- If extend_existing or keep_existing are not set, an error is
+ If ``extend_existing`` or ``keep_existing`` are not set, an error is
raised if additional table modifiers are specified when
the given :class:`.Table` is already present in the :class:`.MetaData`.
+
+ As of version 0.7.4, ``extend_existing`` will work in conjunction
+ with ``autoload=True`` to run a new reflection operation against
+ the database; new :class:`.Column` objects will be produced
+ from database metadata to replace those existing with the same
+ name, and additional :class:`.Column` objects not present
+ in the :class:`.Table` will be added.
+ As is always the case with ``autoload=True``, :class:`.Column`
+ objects can be specified in the same :class:`.Table` constructor,
+ which will take precedence. I.e.::
+
+ Table("mytable", metadata,
+ Column('y', Integer),
+ extend_existing=True,
+ autoload=True,
+ autoload_with=engine
+ )
+
+ The above will overwrite all columns within ``mytable`` which are present
+ in the database, except for ``y`` which will be used as is
+ from the above definition.
:param implicit_returning: True by default - indicates that
RETURNING can be used by default to fetch newly inserted primary key
# we do it after the table is in the singleton dictionary to support
# circular foreign keys
if autoload:
- if autoload_with:
- autoload_with.run_callable(
- autoload_with.dialect.reflecttable,
- self, include_columns
- )
- else:
- bind = _bind_or_error(metadata,
- msg="No engine is bound to this Table's MetaData. "
- "Pass an engine to the Table via "
- "autoload_with=<someengine>, "
- "or associate the MetaData with an engine via "
- "metadata.bind=<someengine>")
- bind.run_callable(
- bind.dialect.reflecttable,
- self, include_columns
- )
+ self._autoload(metadata, autoload_with, include_columns)
# initialize all the column, etc. objects. done after reflection to
# allow user-overrides
self._init_items(*args)
+ def _autoload(self, metadata, autoload_with, include_columns):
+ if autoload_with:
+ autoload_with.run_callable(
+ autoload_with.dialect.reflecttable,
+ self, include_columns
+ )
+ else:
+ bind = _bind_or_error(metadata,
+ msg="No engine is bound to this Table's MetaData. "
+ "Pass an engine to the Table via "
+ "autoload_with=<someengine>, "
+ "or associate the MetaData with an engine via "
+ "metadata.bind=<someengine>")
+ bind.run_callable(
+ bind.dialect.reflecttable,
+ self, include_columns
+ )
+
@property
def _sorted_constraints(self):
"""Return the set of constraints as a list, sorted by creation order."""
if 'info' in kwargs:
self.info = kwargs.pop('info')
+ if autoload:
+ self._autoload(self.metadata, autoload_with, include_columns)
+
self._extra_kwargs(**kwargs)
self._init_items(*args)
from sqlalchemy import types as sql_types
from sqlalchemy import schema, events, event
from sqlalchemy.engine.reflection import Inspector
-from sqlalchemy import MetaData, Integer
+from sqlalchemy import MetaData, Integer, String
from test.lib.schema import Table, Column
import sqlalchemy as sa
from test.lib import ComparesTables, \
for c in ('a', 'c', 'd'):
assert c not in foo.c
+ @testing.provide_metadata
+ def test_extend_existing(self):
+ meta = self.metadata
+
+ t1 = Table('t', meta,
+ Column('x', Integer),
+ Column('y', Integer),
+ Column('z', Integer, server_default="5"),
+ )
+ meta.create_all()
+
+ m2 = MetaData()
+ old_z = Column('z', String)
+ old_y = Column('y', String)
+ old_q = Column('q', Integer)
+ t2 = Table('t', m2, old_z, old_q)
+ t2 = Table('t', m2, old_y,
+ extend_existing=True,
+ autoload=True,
+ autoload_with=testing.db)
+ eq_(
+ set(t2.columns.keys()),
+ set(['x', 'y', 'z', 'q'])
+ )
+ assert t2.c.z is not old_z
+ assert t2.c.y is old_y
+ assert t2.c.z.type._type_affinity is Integer
+ assert t2.c.q is old_q
+
+ m3 = MetaData()
+ t3 = Table('t', m3, Column('z', Integer))
+ t3 = Table('t', m3, extend_existing=False,
+ autoload=True,
+ autoload_with=testing.db)
+ eq_(
+ set(t3.columns.keys()),
+ set(['z'])
+ )
+
@testing.emits_warning(r".*omitted columns")
@testing.provide_metadata
def test_include_columns_indexes(self):