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

View File

@ -30,6 +30,7 @@ Features
Bug fixes
*****************
- ORM methods now accept `connection` parameter commonly used for transactions
- `server_defaults` passed to :meth:`Column.create <migrate.changeset.schema.ChangesetColumn.create>`
are now issued correctly
- 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 \
:keyword:`None` the instance's engine will be used
: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__
self.__do_imports('constraintgenerator', *a, **kw)
@ -50,6 +52,8 @@ class ConstraintChangeset(object):
:param cascade: Issue CASCADE drop if database supports it
:type engine: :class:`sqlalchemy.engine.base.Engine`
: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
"""
self.cascade = kw.pop('cascade', False)
@ -63,7 +67,7 @@ class ConstraintChangeset(object):
class PrimaryKeyConstraint(ConstraintChangeset, schema.PrimaryKeyConstraint):
"""Construct PrimaryKeyConstraint
Migrate's additional parameters:
:param cols: Columns in constraint.
@ -89,7 +93,7 @@ class PrimaryKeyConstraint(ConstraintChangeset, schema.PrimaryKeyConstraint):
class ForeignKeyConstraint(ConstraintChangeset, schema.ForeignKeyConstraint):
"""Construct ForeignKeyConstraint
Migrate's additional parameters:
:param columns: Columns in constraint
@ -132,7 +136,7 @@ class CheckConstraint(ConstraintChangeset, schema.CheckConstraint):
"""Construct CheckConstraint
Migrate's additional parameters:
:param sqltext: Plain SQL text to check condition
:param columns: If not name is applied, you must supply this kw\
to autoname constraint
@ -165,7 +169,7 @@ class CheckConstraint(ConstraintChangeset, schema.CheckConstraint):
class UniqueConstraint(ConstraintChangeset, schema.UniqueConstraint):
"""Construct UniqueConstraint
Migrate's additional parameters:
:param cols: Columns in constraint.

View File

@ -57,15 +57,22 @@ def get_dialect_visitor(sa_dialect, name):
return visitor
def run_single_visitor(engine, visitorcallable, element, **kwargs):
"""Runs only one method on the visitor"""
conn = engine.contextual_connect(close_with_result=False)
def run_single_visitor(engine, visitorcallable, element,
connection=None, **kwargs):
"""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:
visitor = visitorcallable(engine.dialect, conn)
if hasattr(element, '__migrate_visit_name__'):
fn = getattr(visitor, 'visit_' + element.__migrate_visit_name__)
else:
fn = getattr(visitor, 'visit_' + element.__visit_name__)
fn(element, **kwargs)
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.drop(table=self, *p, **kw)
def rename(self, name, *args, **kwargs):
def rename(self, name, connection=None, **kwargs):
"""Rename this table.
:param name: New name of the table.
:type name: string
:param alter_metadata: If True, table will be removed from metadata
: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)
engine = self.bind
self.new_name = name
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
if self.alter_metadata:
@ -485,7 +487,7 @@ class ChangesetColumn(object):
return alter_column(self, *p, **k)
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.
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 populate_default: If True, created column will be \
populated with defaults
:param connection: reuse connection istead of creating new one.
:type table: Table instance
:type index_name: string
:type unique_name: string
:type primary_key_name: string
:type alter_metadata: bool
:type populate_default: bool
:type connection: :class:`sqlalchemy.engine.base.Connection` instance
:returns: self
"""
self.populate_default = kwargs.pop('populate_default', False)
self.alter_metadata = kwargs.pop('alter_metadata', DEFAULT_ALTER_METADATA)
@ -514,26 +520,29 @@ populated with defaults
self.primary_key_name = primary_key_name
for cons in ('index_name', 'unique_name', 'primary_key_name'):
self._check_sanity_constraints(cons)
if self.alter_metadata:
self.add_to_table(table)
engine = self.table.bind
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:
stmt = table.update().values({self: engine._execute_default(self.default)})
engine.execute(stmt)
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.
``ALTER TABLE DROP COLUMN``, for most databases.
:param alter_metadata: If True, column will be removed from table object.
: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)
if table is not None:
@ -542,7 +551,7 @@ populated with defaults
if self.alter_metadata:
self.remove_from_table(self.table, unset_table=False)
visitorcallable = get_engine_visitor(engine, 'columndropper')
engine._run_visitor(visitorcallable, self, *args, **kwargs)
engine._run_visitor(visitorcallable, self, connection, **kwargs)
if self.alter_metadata:
self.table = None
return self
@ -557,7 +566,7 @@ populated with defaults
self.table = None
if table.c.contains_column(self):
table.c.remove(self)
# TODO: this is fixed in 0.6
def copy_fixed(self, **kw):
"""Create a copy of this ``Column``, with all attributes."""
@ -590,19 +599,21 @@ class ChangesetIndex(object):
__visit_name__ = 'index'
def rename(self, name, *args, **kwargs):
def rename(self, name, connection=None, **kwargs):
"""Change the name of an index.
:param name: New name of the Index.
:type name: string
:param alter_metadata: If True, Index object will be altered.
: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)
engine = self.table.bind
self.new_name = name
visitorcallable = get_engine_visitor(engine, 'schemachanger')
engine._run_visitor(visitorcallable, self, *args, **kwargs)
engine._run_visitor(visitorcallable, self, connection, **kwargs)
if self.alter_metadata:
self.name = name