as an extension to the asc() and desc() operators, called
nullsfirst() and nullslast(). [ticket:723]
+ - The Index() construct can be created inline with a Table
+ definition, using strings as column names, as an alternative
+ to the creation of the index outside of the Table.
+
- mssql
- the String/Unicode types, and their counterparts VARCHAR/
NVARCHAR, emit "max" as the length when no length is
more than one column, use the :class:`~sqlalchemy.schema.Index` construct,
which requires a name.
-Note that the :class:`~sqlalchemy.schema.Index` construct is created
-**externally** to the table which it corresponds, using
-:class:`~sqlalchemy.schema.Column` objects and not strings.
-
Below we illustrate a :class:`~sqlalchemy.schema.Table` with several
:class:`~sqlalchemy.schema.Index` objects associated. The DDL for "CREATE
INDEX" is issued right after the create statements for the table:
CREATE UNIQUE INDEX myindex ON mytable (col5, col6)
CREATE INDEX idx_col34 ON mytable (col3, col4){stop}
+Note in the example above, the :class:`.Index` construct is created
+externally to the table which it corresponds, using :class:`.Column`
+objects directly. As of SQLAlchemy 0.7, :class:`.Index` also supports
+"inline" definition inside the :class:`.Table`, using string names to
+identify columns::
+
+ meta = MetaData()
+ mytable = Table('mytable', meta,
+ Column('col1', Integer),
+
+ Column('col2', Integer),
+
+ Column('col3', Integer),
+ Column('col4', Integer),
+
+ # place an index on col1, col2
+ Index('idx_col12', 'col1', 'col2'),
+
+ # place a unique index on col3, col4
+ Index('idx_col34', 'col3', 'col4', unique=True)
+ )
+
The :class:`~sqlalchemy.schema.Index` object also supports its own ``create()`` method:
.. sourcecode:: python+sql
__tablename__='my_model'
@declared_attr
- def __table_args__(self):
+ def __table_args__(cls):
args = dict()
args.update(MySQLSettings.__table_args__)
args.update(MyOtherMixin.__table_args__)
id = Column(Integer, primary_key=True)
+Creating Indexes with Mixins
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To define a named, potentially multicolumn :class:`.Index` that applies to all
+tables derived from a mixin, use the "inline" form of :class:`.Index` and establish
+it as part of ``__table_args__``::
+
+ class MyMixin(object):
+ a = Column(Integer)
+ b = Column(Integer)
+
+ @declared_attr
+ def __table_args__(cls):
+ return (Index('test_idx_%s' % cls.__tablename__, 'a', 'b'),)
+
+ class MyModel(Base,MyMixin):
+ __tablename__ = 'atable'
+ c = Column(Integer,primary_key=True)
+
Class Constructor
=================
def copy(self, **kw):
raise NotImplementedError()
-class ColumnCollectionConstraint(Constraint):
+class ColumnCollectionMixin(object):
+ def __init__(self, *columns):
+ self.columns = expression.ColumnCollection()
+ self._pending_colargs = [_to_schema_column_or_string(c)
+ for c in columns]
+ if self._pending_colargs and \
+ isinstance(self._pending_colargs[0], Column) and \
+ self._pending_colargs[0].table is not None:
+ self._set_parent(self._pending_colargs[0].table)
+
+ def _set_parent(self, table):
+ for col in self._pending_colargs:
+ if isinstance(col, basestring):
+ col = table.c[col]
+ self.columns.add(col)
+
+class ColumnCollectionConstraint(ColumnCollectionMixin, Constraint):
"""A constraint that proxies a ColumnCollection."""
def __init__(self, *columns, **kw):
for this constraint.
"""
- super(ColumnCollectionConstraint, self).__init__(**kw)
- self.columns = expression.ColumnCollection()
- self._pending_colargs = [_to_schema_column_or_string(c)
- for c in columns]
- if self._pending_colargs and \
- isinstance(self._pending_colargs[0], Column) and \
- self._pending_colargs[0].table is not None:
- self._set_parent(self._pending_colargs[0].table)
+ ColumnCollectionMixin.__init__(self, *columns)
+ Constraint.__init__(self, **kw)
def _set_parent(self, table):
- super(ColumnCollectionConstraint, self)._set_parent(table)
- for col in self._pending_colargs:
- if isinstance(col, basestring):
- col = table.c[col]
- self.columns.add(col)
+ ColumnCollectionMixin._set_parent(self, table)
+ Constraint._set_parent(self, table)
def __contains__(self, x):
return x in self.columns
__visit_name__ = 'unique_constraint'
-class Index(SchemaItem):
+class Index(ColumnCollectionMixin, SchemaItem):
"""A table-level INDEX.
Defines a composite (one or more column) INDEX. For a no-frills, single
__visit_name__ = 'index'
- def __init__(self, name, *columns, **kwargs):
+ def __init__(self, name, *columns, **kw):
"""Construct an index object.
:param name:
Other keyword arguments may be interpreted by specific dialects.
"""
-
- self.name = name
- self.columns = expression.ColumnCollection()
self.table = None
- self.unique = kwargs.pop('unique', False)
- self.kwargs = kwargs
-
- for column in columns:
- column = _to_schema_column(column)
- if self.table is None:
- self._set_parent(column.table)
- elif column.table != self.table:
- # all columns muse be from same table
- raise exc.ArgumentError(
- "All index columns must be from same table. "
- "%s is from %s not %s" %
- (column, column.table, self.table))
- self.columns.add(column)
+ # will call _set_parent() if table-bound column
+ # objects are present
+ ColumnCollectionMixin.__init__(self, *columns)
+ self.name = name
+ self.unique = kw.pop('unique', False)
+ self.kwargs = kw
def _set_parent(self, table):
+ ColumnCollectionMixin._set_parent(self, table)
+
+ if self.table is not None and table is not self.table:
+ raise exc.ArgumentError(
+ "Index '%s' is against table '%s', and "
+ "cannot be associated with table '%s'." % (
+ self.name,
+ self.table.description,
+ table.description
+ )
+ )
self.table = table
+ for c in self.columns:
+ if c.table != self.table:
+ raise exc.ArgumentError(
+ "Column '%s' is not part of table '%s'." %
+ (c, self.table.description)
+ )
table.indexes.add(self)
@property
dialect=dialect
)
+ def test_index_declartion_inline(self):
+ t1 = Table('t1', metadata,
+ Column('x', Integer),
+ Column('y', Integer),
+ Index('foo', 'x', 'y')
+ )
+ self.assert_compile(
+ schema.CreateIndex(list(t1.indexes)[0]),
+ "CREATE INDEX foo ON t1 (x, y)"
+ )
+
+ def test_index_asserts_cols_standalone(self):
+ t1 = Table('t1', metadata,
+ Column('x', Integer)
+ )
+ t2 = Table('t2', metadata,
+ Column('y', Integer)
+ )
+ assert_raises_message(
+ exc.ArgumentError,
+ "Column 't2.y' is not part of table 't1'.",
+ Index,
+ "bar", t1.c.x, t2.c.y
+ )
+
+ def test_index_asserts_cols_inline(self):
+ t1 = Table('t1', metadata,
+ Column('x', Integer)
+ )
+ assert_raises_message(
+ exc.ArgumentError,
+ "Index 'bar' is against table 't1', and "
+ "cannot be associated with table 't2'.",
+ Table, 't2', metadata,
+ Column('y', Integer),
+ Index('bar', t1.c.x)
+ )
class ConstraintCompilationTest(TestBase, AssertsCompiledSQL):