.. changelog::
:version: 0.8.0
+ .. change::
+ :tags: feature, sql
+ :tickets: 2657
+
+ Added a new argument to :class:`.Enum` and its base
+ :class:`.SchemaType` ``inherit_schema``. When set to ``True``,
+ the type will set its ``schema`` attribute of that of the
+ :class:`.Table` to which it is associated. This also occurs
+ during a :meth:`.Table.tometadata` operation; the :class:`.SchemaType`
+ is now copied in all cases when :meth:`.Table.tometadata` happens,
+ and if ``inherit_schema=True``, the type will take on the new
+ schema name passed to the method. The ``schema`` is important
+ when used with the Postgresql backend, as the type results in
+ a ``CREATE TYPE`` statement.
+
.. change::
:tags: feature, postgresql
:pullreq: 40
kw.pop('name', None)
kw.pop('quote', None)
kw.pop('native_enum', None)
+ kw.pop('inherit_schema', None)
_StringType.__init__(self, length=length, **kw)
sqltypes.Enum.__init__(self, *enums)
E.g.::
+ some_engine = create_engine("sqlite:///some.db")
+
# create two metadata
- meta1 = MetaData('sqlite:///querytest.db')
+ meta1 = MetaData()
meta2 = MetaData()
# load 'users' from the sqlite engine
- users_table = Table('users', meta1, autoload=True)
+ users_table = Table('users', meta1, autoload=True,
+ autoload_with=some_engine)
# create the same Table object for the plain metadata
users_table_2 = users_table.tometadata(meta2)
+ :param metadata: Target :class:`.MetaData` object.
+ :param schema: Optional string name of a target schema, or
+ ``None`` for no schema. The :class:`.Table` object will be
+ given this schema name upon copy. Defaults to the special
+ symbol :attr:`.RETAIN_SCHEMA` which indicates no change should be
+ made to the schema name of the resulting :class:`.Table`.
+
"""
if schema is RETAIN_SCHEMA:
[c.copy(**kw) for c in self.constraints] + \
[c.copy(**kw) for c in self.foreign_keys if not c.constraint]
+ type_ = self.type
+ if isinstance(type_, sqltypes.SchemaType):
+ type_ = type_.copy(**kw)
+
c = self._constructor(
name=self.name,
- type_=self.type,
+ type_=type_,
key=self.key,
primary_key=self.primary_key,
nullable=self.nullable,
surrounding the association of the type object with a parent
:class:`.Column`.
+ .. seealso::
+
+ :class:`.Enum`
+
+ :class:`.Boolean`
+
+
"""
def __init__(self, **kw):
self.quote = kw.pop('quote', None)
self.schema = kw.pop('schema', None)
self.metadata = kw.pop('metadata', None)
+ self.inherit_schema = kw.pop('inherit_schema', False)
if self.metadata:
event.listen(
self.metadata,
column._on_table_attach(util.portable_instancemethod(self._set_table))
def _set_table(self, column, table):
+ if self.inherit_schema:
+ self.schema = table.schema
+
event.listen(
table,
"before_create",
util.portable_instancemethod(self._on_metadata_drop)
)
+ def copy(self, **kw):
+ return self.adapt(self.__class__)
+
+ def adapt(self, impltype, **kw):
+ schema = kw.pop('schema', self.schema)
+ metadata = kw.pop('metadata', self.metadata)
+ return impltype(name=self.name,
+ quote=self.quote,
+ schema=schema,
+ metadata=metadata,
+ inherit_schema=self.inherit_schema,
+ **kw
+ )
+
@property
def bind(self):
return self.metadata and self.metadata.bind or None
By default, uses the backend's native ENUM type if available,
else uses VARCHAR + a CHECK constraint.
- See also:
+ .. seealso::
:class:`~.postgresql.ENUM` - PostgreSQL-specific type,
which has additional functionality.
available. Defaults to True. When False, uses VARCHAR + check
constraint for all backends.
- :param schema: Schemaname of this type. For types that exist on the
+ :param schema: Schema name of this type. For types that exist on the
target database as an independent schema construct (Postgresql),
this parameter specifies the named schema in which the type is
present.
+ .. note::
+
+ The ``schema`` of the :class:`.Enum` type does not
+ by default make use of the ``schema`` established on the
+ owning :class:`.Table`. If this behavior is desired,
+ set the ``inherit_schema`` flag to ``True``.
+
:param quote: Force quoting to be on or off on the type's name. If
left as the default of `None`, the usual schema-level "case
sensitive"/"reserved name" rules are used to determine if this
type's name should be quoted.
+ :param inherit_schema: When ``True``, the "schema" from the owning
+ :class:`.Table` will be copied to the "schema" attribute of this
+ :class:`.Enum`, replacing whatever value was passed for the
+ ``schema`` attribute. This also takes effect when using the
+ :meth:`.Table.tometadata` operation.
+
+ .. versionadded:: 0.8
+
"""
self.enums = enums
self.native_enum = kw.pop('native_enum', True)
table.append_constraint(e)
def adapt(self, impltype, **kw):
+ schema = kw.pop('schema', self.schema)
+ metadata = kw.pop('metadata', self.metadata)
if issubclass(impltype, Enum):
return impltype(name=self.name,
quote=self.quote,
- schema=self.schema,
- metadata=self.metadata,
+ schema=schema,
+ metadata=metadata,
convert_unicode=self.convert_unicode,
native_enum=self.native_enum,
+ inherit_schema=self.inherit_schema,
*self.enums,
**kw
)
from sqlalchemy import Integer, String, UniqueConstraint, \
CheckConstraint, ForeignKey, MetaData, Sequence, \
ForeignKeyConstraint, ColumnDefault, Index, event,\
- events, Unicode
+ events, Unicode, types as sqltypes
from sqlalchemy.testing.schema import Table, Column
from sqlalchemy import schema, exc
import sqlalchemy as tsa
a2 = a.tometadata(m2)
assert b2.c.y.references(a2.c.x)
+
def test_pickle_metadata_sequence_restated(self):
m1 = MetaData()
Table('a', m1,
)
is_(t._autoincrement_column, t.c.id)
+class SchemaTypeTest(fixtures.TestBase):
+ class MyType(sqltypes.SchemaType, sqltypes.TypeEngine):
+ column = None
+ table = None
+ evt_targets = ()
+
+ def _set_table(self, column, table):
+ super(SchemaTypeTest.MyType, self)._set_table(column, table)
+ self.column = column
+ self.table = table
+
+ def _on_table_create(self, target, bind, **kw):
+ self.evt_targets += (target,)
+
+ def test_independent_schema(self):
+ m = MetaData()
+ type_ = self.MyType(schema="q")
+ t1 = Table('x', m, Column("y", type_), schema="z")
+ eq_(t1.c.y.type.schema, "q")
+
+ def test_inherit_schema(self):
+ m = MetaData()
+ type_ = self.MyType(schema="q", inherit_schema=True)
+ t1 = Table('x', m, Column("y", type_), schema="z")
+ eq_(t1.c.y.type.schema, "z")
+
+ def test_independent_schema_enum(self):
+ m = MetaData()
+ type_ = sqltypes.Enum("a", schema="q")
+ t1 = Table('x', m, Column("y", type_), schema="z")
+ eq_(t1.c.y.type.schema, "q")
+
+ def test_inherit_schema_enum(self):
+ m = MetaData()
+ type_ = sqltypes.Enum("a", "b", "c", schema="q", inherit_schema=True)
+ t1 = Table('x', m, Column("y", type_), schema="z")
+ eq_(t1.c.y.type.schema, "z")
+
+ def test_tometadata_copy_type(self):
+ m1 = MetaData()
+
+ type_ = self.MyType()
+ t1 = Table('x', m1, Column("y", type_))
+
+ m2 = MetaData()
+ t2 = t1.tometadata(m2)
+
+ # metadata isn't set
+ is_(t2.c.y.type.metadata, None)
+
+ # our test type sets table, though
+ is_(t2.c.y.type.table, t2)
+
+ def test_tometadata_independent_schema(self):
+ m1 = MetaData()
+
+ type_ = self.MyType()
+ t1 = Table('x', m1, Column("y", type_))
+
+ m2 = MetaData()
+ t2 = t1.tometadata(m2, schema="bar")
+
+ eq_(t2.c.y.type.schema, None)
+
+ def test_tometadata_inherit_schema(self):
+ m1 = MetaData()
+
+ type_ = self.MyType(inherit_schema=True)
+ t1 = Table('x', m1, Column("y", type_))
+
+ m2 = MetaData()
+ t2 = t1.tometadata(m2, schema="bar")
+
+ eq_(t1.c.y.type.schema, None)
+ eq_(t2.c.y.type.schema, "bar")
+
+ def test_tometadata_independent_events(self):
+ m1 = MetaData()
+
+ type_ = self.MyType()
+ t1 = Table('x', m1, Column("y", type_))
+
+ m2 = MetaData()
+ t2 = t1.tometadata(m2)
+
+ t1.dispatch.before_create(t1, testing.db)
+ eq_(t1.c.y.type.evt_targets, (t1,))
+ eq_(t2.c.y.type.evt_targets, ())
+
+ t2.dispatch.before_create(t2, testing.db)
+ t2.dispatch.before_create(t2, testing.db)
+ eq_(t1.c.y.type.evt_targets, (t1,))
+ eq_(t2.c.y.type.evt_targets, (t2, t2))
+
+
+
class SchemaTest(fixtures.TestBase, AssertsCompiledSQL):
def test_default_schema_metadata_fk(self):