adding connection keyword to ORM methods

This commit is contained in:
iElectric 2010-07-11 17:22:28 +02:00
parent b0157137e2
commit a3d3470d5e
5 changed files with 46 additions and 26 deletions

11
TODO
View File

@ -10,11 +10,7 @@ make_update_script_for_model:
0.6.0 0.6.0
- make logging stderr and stdout aware
- update documentation
- update repository migration script - update repository migration script
- readd transaction support
- wrap migration into transaction
- interactive migration script resolution - interactive migration script resolution
- port to unittest2 - port to unittest2
@ -26,6 +22,7 @@ make_update_script_for_model:
- verbose output on migration failures - verbose output on migration failures
Documentation Transaction support in 0.6.1
- script.run should call engine.transaction()
- better document 'populate_default' - API should support engine and connection as well
- tests for transactions

View File

@ -30,6 +30,7 @@ Features
Bug fixes Bug fixes
***************** *****************
- ORM methods now accept `connection` parameter commonly used for transactions
- `server_defaults` passed to :meth:`Column.create <migrate.changeset.schema.ChangesetColumn.create>` - `server_defaults` passed to :meth:`Column.create <migrate.changeset.schema.ChangesetColumn.create>`
are now issued correctly are now issued correctly
- use SQLAlchemy quoting system to avoid name conflicts (for issue 32) - use SQLAlchemy quoting system to avoid name conflicts (for issue 32)

View File

@ -38,6 +38,8 @@ class ConstraintChangeset(object):
:param engine: the database engine to use. If this is \ :param engine: the database engine to use. If this is \
:keyword:`None` the instance's engine will be used :keyword:`None` the instance's engine will be used
:type engine: :class:`sqlalchemy.engine.base.Engine` :type engine: :class:`sqlalchemy.engine.base.Engine`
:param connection: reuse connection istead of creating new one.
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
""" """
# TODO: set the parent here instead of in __init__ # TODO: set the parent here instead of in __init__
self.__do_imports('constraintgenerator', *a, **kw) self.__do_imports('constraintgenerator', *a, **kw)
@ -50,6 +52,8 @@ class ConstraintChangeset(object):
:param cascade: Issue CASCADE drop if database supports it :param cascade: Issue CASCADE drop if database supports it
:type engine: :class:`sqlalchemy.engine.base.Engine` :type engine: :class:`sqlalchemy.engine.base.Engine`
:type cascade: bool :type cascade: bool
:param connection: reuse connection istead of creating new one.
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
:returns: Instance with cleared columns :returns: Instance with cleared columns
""" """
self.cascade = kw.pop('cascade', False) self.cascade = kw.pop('cascade', False)
@ -63,7 +67,7 @@ class ConstraintChangeset(object):
class PrimaryKeyConstraint(ConstraintChangeset, schema.PrimaryKeyConstraint): class PrimaryKeyConstraint(ConstraintChangeset, schema.PrimaryKeyConstraint):
"""Construct PrimaryKeyConstraint """Construct PrimaryKeyConstraint
Migrate's additional parameters: Migrate's additional parameters:
:param cols: Columns in constraint. :param cols: Columns in constraint.
@ -89,7 +93,7 @@ class PrimaryKeyConstraint(ConstraintChangeset, schema.PrimaryKeyConstraint):
class ForeignKeyConstraint(ConstraintChangeset, schema.ForeignKeyConstraint): class ForeignKeyConstraint(ConstraintChangeset, schema.ForeignKeyConstraint):
"""Construct ForeignKeyConstraint """Construct ForeignKeyConstraint
Migrate's additional parameters: Migrate's additional parameters:
:param columns: Columns in constraint :param columns: Columns in constraint
@ -132,7 +136,7 @@ class CheckConstraint(ConstraintChangeset, schema.CheckConstraint):
"""Construct CheckConstraint """Construct CheckConstraint
Migrate's additional parameters: Migrate's additional parameters:
:param sqltext: Plain SQL text to check condition :param sqltext: Plain SQL text to check condition
:param columns: If not name is applied, you must supply this kw\ :param columns: If not name is applied, you must supply this kw\
to autoname constraint to autoname constraint
@ -165,7 +169,7 @@ class CheckConstraint(ConstraintChangeset, schema.CheckConstraint):
class UniqueConstraint(ConstraintChangeset, schema.UniqueConstraint): class UniqueConstraint(ConstraintChangeset, schema.UniqueConstraint):
"""Construct UniqueConstraint """Construct UniqueConstraint
Migrate's additional parameters: Migrate's additional parameters:
:param cols: Columns in constraint. :param cols: Columns in constraint.

View File

@ -57,15 +57,22 @@ def get_dialect_visitor(sa_dialect, name):
return visitor return visitor
def run_single_visitor(engine, visitorcallable, element, **kwargs): def run_single_visitor(engine, visitorcallable, element,
"""Runs only one method on the visitor""" connection=None, **kwargs):
conn = engine.contextual_connect(close_with_result=False) """Taken from :meth:`sqlalchemy.engine.base.Engine._run_single_visitor`
with support for migrate visitors.
"""
if connection is None:
conn = engine.contextual_connect(close_with_result=False)
else:
conn = connection
visitor = visitorcallable(engine.dialect, conn)
try: try:
visitor = visitorcallable(engine.dialect, conn)
if hasattr(element, '__migrate_visit_name__'): if hasattr(element, '__migrate_visit_name__'):
fn = getattr(visitor, 'visit_' + element.__migrate_visit_name__) fn = getattr(visitor, 'visit_' + element.__migrate_visit_name__)
else: else:
fn = getattr(visitor, 'visit_' + element.__visit_name__) fn = getattr(visitor, 'visit_' + element.__visit_name__)
fn(element, **kwargs) fn(element, **kwargs)
finally: finally:
conn.close() if connection is None:
conn.close()

View File

@ -426,19 +426,21 @@ class ChangesetTable(object):
column = sqlalchemy.Column(str(column), sqlalchemy.Integer()) column = sqlalchemy.Column(str(column), sqlalchemy.Integer())
column.drop(table=self, *p, **kw) column.drop(table=self, *p, **kw)
def rename(self, name, *args, **kwargs): def rename(self, name, connection=None, **kwargs):
"""Rename this table. """Rename this table.
:param name: New name of the table. :param name: New name of the table.
:type name: string :type name: string
:param alter_metadata: If True, table will be removed from metadata :param alter_metadata: If True, table will be removed from metadata
:type alter_metadata: bool :type alter_metadata: bool
:param connection: reuse connection istead of creating new one.
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
""" """
self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA)
engine = self.bind engine = self.bind
self.new_name = name self.new_name = name
visitorcallable = get_engine_visitor(engine, 'schemachanger') visitorcallable = get_engine_visitor(engine, 'schemachanger')
run_single_visitor(engine, visitorcallable, self, *args, **kwargs) run_single_visitor(engine, visitorcallable, self, connection, **kwargs)
# Fix metadata registration # Fix metadata registration
if self.alter_metadata: if self.alter_metadata:
@ -485,7 +487,7 @@ class ChangesetColumn(object):
return alter_column(self, *p, **k) return alter_column(self, *p, **k)
def create(self, table=None, index_name=None, unique_name=None, def create(self, table=None, index_name=None, unique_name=None,
primary_key_name=None, *args, **kwargs): primary_key_name=None, connection=None, **kwargs):
"""Create this column in the database. """Create this column in the database.
Assumes the given table exists. ``ALTER TABLE ADD COLUMN``, Assumes the given table exists. ``ALTER TABLE ADD COLUMN``,
@ -500,12 +502,16 @@ class ChangesetColumn(object):
:param alter_metadata: If True, column will be added to table object. :param alter_metadata: If True, column will be added to table object.
:param populate_default: If True, created column will be \ :param populate_default: If True, created column will be \
populated with defaults populated with defaults
:param connection: reuse connection istead of creating new one.
:type table: Table instance :type table: Table instance
:type index_name: string :type index_name: string
:type unique_name: string :type unique_name: string
:type primary_key_name: string :type primary_key_name: string
:type alter_metadata: bool :type alter_metadata: bool
:type populate_default: bool :type populate_default: bool
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
:returns: self
""" """
self.populate_default = kwargs.pop('populate_default', False) self.populate_default = kwargs.pop('populate_default', False)
self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA)
@ -514,26 +520,29 @@ populated with defaults
self.primary_key_name = primary_key_name self.primary_key_name = primary_key_name
for cons in ('index_name', 'unique_name', 'primary_key_name'): for cons in ('index_name', 'unique_name', 'primary_key_name'):
self._check_sanity_constraints(cons) self._check_sanity_constraints(cons)
if self.alter_metadata: if self.alter_metadata:
self.add_to_table(table) self.add_to_table(table)
engine = self.table.bind engine = self.table.bind
visitorcallable = get_engine_visitor(engine, 'columngenerator') visitorcallable = get_engine_visitor(engine, 'columngenerator')
engine._run_visitor(visitorcallable, self, *args, **kwargs) engine._run_visitor(visitorcallable, self, connection, **kwargs)
# TODO: reuse existing connection
if self.populate_default and self.default is not None: if self.populate_default and self.default is not None:
stmt = table.update().values({self: engine._execute_default(self.default)}) stmt = table.update().values({self: engine._execute_default(self.default)})
engine.execute(stmt) engine.execute(stmt)
return self return self
def drop(self, table=None, *args, **kwargs): def drop(self, table=None, connection=None, **kwargs):
"""Drop this column from the database, leaving its table intact. """Drop this column from the database, leaving its table intact.
``ALTER TABLE DROP COLUMN``, for most databases. ``ALTER TABLE DROP COLUMN``, for most databases.
:param alter_metadata: If True, column will be removed from table object. :param alter_metadata: If True, column will be removed from table object.
:type alter_metadata: bool :type alter_metadata: bool
:param connection: reuse connection istead of creating new one.
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
""" """
self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA)
if table is not None: if table is not None:
@ -542,7 +551,7 @@ populated with defaults
if self.alter_metadata: if self.alter_metadata:
self.remove_from_table(self.table, unset_table=False) self.remove_from_table(self.table, unset_table=False)
visitorcallable = get_engine_visitor(engine, 'columndropper') visitorcallable = get_engine_visitor(engine, 'columndropper')
engine._run_visitor(visitorcallable, self, *args, **kwargs) engine._run_visitor(visitorcallable, self, connection, **kwargs)
if self.alter_metadata: if self.alter_metadata:
self.table = None self.table = None
return self return self
@ -557,7 +566,7 @@ populated with defaults
self.table = None self.table = None
if table.c.contains_column(self): if table.c.contains_column(self):
table.c.remove(self) table.c.remove(self)
# TODO: this is fixed in 0.6 # TODO: this is fixed in 0.6
def copy_fixed(self, **kw): def copy_fixed(self, **kw):
"""Create a copy of this ``Column``, with all attributes.""" """Create a copy of this ``Column``, with all attributes."""
@ -590,19 +599,21 @@ class ChangesetIndex(object):
__visit_name__ = 'index' __visit_name__ = 'index'
def rename(self, name, *args, **kwargs): def rename(self, name, connection=None, **kwargs):
"""Change the name of an index. """Change the name of an index.
:param name: New name of the Index. :param name: New name of the Index.
:type name: string :type name: string
:param alter_metadata: If True, Index object will be altered. :param alter_metadata: If True, Index object will be altered.
:type alter_metadata: bool :type alter_metadata: bool
:param connection: reuse connection istead of creating new one.
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
""" """
self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA) self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA)
engine = self.table.bind engine = self.table.bind
self.new_name = name self.new_name = name
visitorcallable = get_engine_visitor(engine, 'schemachanger') visitorcallable = get_engine_visitor(engine, 'schemachanger')
engine._run_visitor(visitorcallable, self, *args, **kwargs) engine._run_visitor(visitorcallable, self, connection, **kwargs)
if self.alter_metadata: if self.alter_metadata:
self.name = name self.name = name