diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-11-15 13:07:40 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2011-11-15 13:07:40 -0500 |
commit | 2c05c7e0de90eaea23cd7e34b4405c2915a25171 (patch) | |
tree | 04a90fc89bca75c05a3035916051da8e37bcc455 /alembic/op.py | |
parent | b5facd3a575afd1c2f99c8efcf888a077804c300 (diff) | |
download | alembic-2c05c7e0de90eaea23cd7e34b4405c2915a25171.tar.gz |
- remove add_constraint, this is not the current philosophy of the op package
- document most op methods
- add support for create_index, drop_index
- remove needless arguments from drop_table
- propagate arguemnts to UniqueConstraint
Diffstat (limited to 'alembic/op.py')
-rw-r--r-- | alembic/op.py | 188 |
1 files changed, 158 insertions, 30 deletions
diff --git a/alembic/op.py b/alembic/op.py index 4963122..cb016e8 100644 --- a/alembic/op.py +++ b/alembic/op.py @@ -5,19 +5,20 @@ from sqlalchemy import schema, sql util.importlater.resolve_all() -__all__ = [ +__all__ = sorted([ 'alter_column', 'add_column', 'drop_column', - 'add_constraint', 'create_foreign_key', 'create_table', 'drop_table', + 'drop_index', + 'create_index', 'bulk_insert', 'create_unique_constraint', 'get_context', 'get_bind', - 'execute'] + 'execute']) def _foreign_key_constraint(name, source, referent, local_cols, remote_cols): m = schema.MetaData() @@ -35,21 +36,10 @@ def _foreign_key_constraint(name, source, referent, local_cols, remote_cols): return f -def _ensure_table_for_constraint(name, constraint): - if getattr(constraint, 'parent', None) is not None: - return - if isinstance(constraint, schema.UniqueConstraint): - # TODO: what if constraint has Column objects already - columns = [schema.Column(n, NULLTYPE) for n in - constraint._pending_colargs] - else: - columns = [] - return schema.Table(name, schema.MetaData(), *(columns + [constraint]) ) - -def _unique_constraint(name, source, local_cols): +def _unique_constraint(name, source, local_cols, **kw): t = schema.Table(source, schema.MetaData(), *[schema.Column(n, NULLTYPE) for n in local_cols]) - return schema.UniqueConstraint(*t.c, name=name) + return schema.UniqueConstraint(*t.c, name=name, **kw) def _table(name, *columns, **kw): m = schema.MetaData() @@ -61,6 +51,12 @@ def _table(name, *columns, **kw): def _column(name, type_, **kw): return schema.Column(name, type_, **kw) +def _index(name, tablename, columns, **kw): + t = schema.Table(tablename, schema.MetaData(), + *[schema.Column(n, NULLTYPE) for n in columns] + ) + return schema.Index(name, *list(t.c), **kw) + def _ensure_table_for_fk(metadata, fk): """create a placeholder Table object for the referent of a ForeignKey. @@ -102,11 +98,31 @@ def add_column(table_name, column): """Issue an "add column" instruction using the current change context. e.g.:: + + from alembic.op import add_column + from sqlalchemy import Column, String + + add_column('organization', + Column('name', String()) + ) + + The provided :class:`~sqlalchemy.schema.Column` object can also + specify a :class:`~sqlalchemy.schema.ForeignKey`, referencing + a remote table name. Alembic will automatically generate a stub + "referenced" table and emit a second ALTER statement in order + to add the constraint separately:: + from alembic.op import add_column + from sqlalchemy import Column, INTEGER, ForeignKey + add_column('organization', Column('account_id', INTEGER, ForeignKey('accounts.id')) ) + :param table_name: String name of the parent table. + :param column: a :class:`sqlalchemy.schema.Column` object + representing the new column. + """ t = _table(table_name, column) @@ -131,27 +147,84 @@ def drop_column(table_name, column_name): _column(column_name, NULLTYPE) ) -def add_constraint(table_name, constraint): - """Issue an "add constraint" instruction using the current change context.""" - - _ensure_table_for_constraint(table_name, constraint) - get_impl().add_constraint( - constraint - ) def create_foreign_key(name, source, referent, local_cols, remote_cols): - """Issue a "create foreign key" instruction using the current change context.""" + """Issue a "create foreign key" instruction using the + current change context. + + e.g.:: + + from alembic.op import create_foreign_key + create_foreign_key("fk_user_address", "address", "user", ["user_id"], ["id"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.ForeignKeyConstraint` + object which it then associates with the :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param name: Name of the foreign key constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + `NamingConventions <http://www.sqlalchemy.org/trac/wiki/UsageRecipes/NamingConventions>`_, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param source: String name of the source table. Currently + there is no support for dotted schema names. + :param referent: String name of the destination table. Currently + there is no support for dotted schema names. + :param local_cols: a list of string column names in the + source table. + :param remote_cols: a list of string column names in the + remote table. + + """ get_impl().add_constraint( _foreign_key_constraint(name, source, referent, local_cols, remote_cols) ) -def create_unique_constraint(name, source, local_cols): - """Issue a "create unique constraint" instruction using the current change context.""" +def create_unique_constraint(name, source, local_cols, **kw): + """Issue a "create unique constraint" instruction using the current change context. + + e.g.:: + + from alembic.op import create_unique_constraint + create_unique_constraint("uq_user_name", "user", ["name"]) + + This internally generates a :class:`~sqlalchemy.schema.Table` object + containing the necessary columns, then generates a new + :class:`~sqlalchemy.schema.UniqueConstraint` + object which it then associates with the :class:`~sqlalchemy.schema.Table`. + Any event listeners associated with this action will be fired + off normally. The :class:`~sqlalchemy.schema.AddConstraint` + construct is ultimately used to generate the ALTER statement. + + :param name: Name of the unique constraint. The name is necessary + so that an ALTER statement can be emitted. For setups that + use an automated naming scheme such as that described at + `NamingConventions <http://www.sqlalchemy.org/trac/wiki/UsageRecipes/NamingConventions>`_, + ``name`` here can be ``None``, as the event listener will + apply the name to the constraint object when it is associated + with the table. + :param source: String name of the source table. Currently + there is no support for dotted schema names. + :param local_cols: a list of string column names in the + source table. + :param deferrable: optional bool. If set, emit DEFERRABLE or NOT DEFERRABLE when + issuing DDL for this constraint. + :param initially: optional string. If set, emit INITIALLY <value> when issuing DDL + for this constraint. + + """ get_impl().add_constraint( - _unique_constraint(name, source, local_cols) + _unique_constraint(name, source, local_cols, + **kw) ) def create_table(name, *columns, **kw): @@ -177,7 +250,7 @@ def create_table(name, *columns, **kw): _table(name, *columns, **kw) ) -def drop_table(name, *columns, **kw): +def drop_table(name): """Issue a "drop table" instruction using the current change context. @@ -187,9 +260,34 @@ def drop_table(name, *columns, **kw): """ get_impl().drop_table( - _table(name, *columns, **kw) + _table(name) ) +def create_index(name, tablename, *columns, **kw): + """Issue a "create index" instruction using the current change context. + + e.g.:: + + from alembic.op import create_index + create_index('ik_test', 't1', ['foo', 'bar']) + + """ + + get_impl().create_index( + _index(name, tablename, *columns, **kw) + ) + +def drop_index(name): + """Issue a "drop index" instruction using the current change context. + + + e.g.:: + + drop_index("accounts") + + """ + get_impl().drop_index(_index(name, 'foo', [])) + def bulk_insert(table, rows): """Issue a "bulk insert" operation using the current change context. @@ -218,7 +316,37 @@ def execute(sql): """Execute the given SQL using the current change context. In a SQL script context, the statement is emitted directly to the - output stream. + output stream. There is *no* return result, however, as this + function is oriented towards generating a change script + that can run in "offline" mode. For full interaction + with a connected database, use the "bind" available + from the context:: + + from alembic.op import get_bind + connection = get_bind() + + Also note that any parameterized statement here *will not work* + in offline mode - any kind of UPDATE or DELETE needs to render + inline expressions. Due to these limitations, + :func:`.execute` is overall not spectacularly useful for migration + scripts that wish to run in offline mode. Consider using the Alembic + directives, or if the environment is only meant to run in + "online" mode, use the ``get_context().bind``. + + :param sql: Any legal SQLAlchemy expression, including: + + * a string + * a :func:`sqlalchemy.sql.expression.text` construct, with the caveat that + bound parameters won't work correctly in offline mode. + * a :func:`sqlalchemy.sql.expression.insert` construct. If working + in offline mode, consider using :func:`alembic.op.bulk_insert` + instead to support parameterization. + * a :func:`sqlalchemy.sql.expression.update`, :func:`sqlalchemy.sql.expression.insert`, + or :func:`sqlalchemy.sql.expression.delete` construct, with the caveat + that bound parameters won't work correctly in offline mode. + * Pretty much anything that's "executable" as described + in :ref:`sqlexpression_toplevel`. + """ get_impl().execute(sql) |