- Added support for type comparison functions to be not just per
environment, but also present on the custom types themselves, by supplying a method ``compare_against_backend``. Added a new documentation section :ref:`compare_types` describing type comparison fully. fixes #296
This commit is contained in:
parent
a192c345fc
commit
dabc7f0932
|
@ -247,6 +247,12 @@ class DefaultImpl(with_metaclass(ImplMeta)):
|
|||
# fixed in 0.7.4
|
||||
metadata_impl.__dict__.pop('_type_affinity', None)
|
||||
|
||||
if hasattr(metadata_impl, "compare_against_backend"):
|
||||
comparison = metadata_impl.compare_against_backend(
|
||||
self.dialect, conn_type)
|
||||
if comparison is not None:
|
||||
return not comparison
|
||||
|
||||
if conn_type._compare_type_affinity(
|
||||
metadata_impl
|
||||
):
|
||||
|
|
|
@ -417,38 +417,14 @@ class EnvironmentContext(object):
|
|||
operation. Defaults to ``False`` which disables type
|
||||
comparison. Set to
|
||||
``True`` to turn on default type comparison, which has varied
|
||||
accuracy depending on backend.
|
||||
|
||||
To customize type comparison behavior, a callable may be
|
||||
specified which
|
||||
can filter type comparisons during an autogenerate operation.
|
||||
The format of this callable is::
|
||||
|
||||
def my_compare_type(context, inspected_column,
|
||||
metadata_column, inspected_type, metadata_type):
|
||||
# return True if the types are different,
|
||||
# False if not, or None to allow the default implementation
|
||||
# to compare these types
|
||||
return None
|
||||
|
||||
context.configure(
|
||||
# ...
|
||||
compare_type = my_compare_type
|
||||
)
|
||||
|
||||
|
||||
``inspected_column`` is a :class:`sqlalchemy.schema.Column` as
|
||||
returned by
|
||||
:meth:`sqlalchemy.engine.reflection.Inspector.reflecttable`,
|
||||
whereas ``metadata_column`` is a
|
||||
:class:`sqlalchemy.schema.Column` from the local model
|
||||
environment.
|
||||
|
||||
A return value of ``None`` indicates to allow default type
|
||||
comparison to proceed.
|
||||
accuracy depending on backend. See :ref:`compare_types`
|
||||
for an example as well as information on other type
|
||||
comparison options.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`compare_types`
|
||||
|
||||
:paramref:`.EnvironmentContext.configure.compare_server_default`
|
||||
|
||||
:param compare_server_default: Indicates server default comparison
|
||||
|
|
|
@ -126,7 +126,7 @@ Autogenerate can **optionally detect**:
|
|||
The feature works well in most cases,
|
||||
but is off by default so that it can be tested on the target schema
|
||||
first. It can also be customized by passing a callable here; see the
|
||||
function's documentation for details.
|
||||
section :ref:`compare_types` for details.
|
||||
* Change of server default. This will occur if you set
|
||||
the :paramref:`.EnvironmentContext.configure.compare_server_default`
|
||||
parameter to ``True``, or to a custom callable function.
|
||||
|
@ -170,10 +170,10 @@ Autogenerate can't currently, but **will eventually detect**:
|
|||
* Sequence additions, removals - not yet implemented.
|
||||
|
||||
|
||||
Rendering Types
|
||||
----------------
|
||||
Comparing and Rendering Types
|
||||
------------------------------
|
||||
|
||||
The area of autogenerate's behavior of rendering Python-based type objects
|
||||
The area of autogenerate's behavior of comparing and rendering Python-based type objects
|
||||
in migration scripts presents a challenge, in that there's
|
||||
a very wide variety of types to be rendered in scripts, including those
|
||||
part of SQLAlchemy as well as user-defined types. A few options
|
||||
|
@ -345,3 +345,76 @@ The finished migration script will include our imports where the
|
|||
op.add_column('sometable', Column('mycolumn', types.MySpecialType()))
|
||||
|
||||
|
||||
.. _compare_types:
|
||||
|
||||
Comparing Types
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The default type comparison logic will work for SQLAlchemy built in types as
|
||||
well as basic user defined types. This logic is only enabled if the
|
||||
:paramref:`.EnvironmentContext.configure.compare_type` parameter
|
||||
is set to True::
|
||||
|
||||
context.configure(
|
||||
# ...
|
||||
compare_type = True
|
||||
)
|
||||
|
||||
Alternatively, the :paramref:`.EnvironmentContext.configure.compare_type`
|
||||
parameter accepts a callable function which may be used to implement custom type
|
||||
comparison logic, for cases such as where special user defined types
|
||||
are being used::
|
||||
|
||||
def my_compare_type(context, inspected_column,
|
||||
metadata_column, inspected_type, metadata_type):
|
||||
# return True if the types are different,
|
||||
# False if not, or None to allow the default implementation
|
||||
# to compare these types
|
||||
return None
|
||||
|
||||
context.configure(
|
||||
# ...
|
||||
compare_type = my_compare_type
|
||||
)
|
||||
|
||||
Above, ``inspected_column`` is a :class:`sqlalchemy.schema.Column` as
|
||||
returned by
|
||||
:meth:`sqlalchemy.engine.reflection.Inspector.reflecttable`, whereas
|
||||
``metadata_column`` is a :class:`sqlalchemy.schema.Column` from the
|
||||
local model environment. A return value of ``None`` indicates that default
|
||||
type comparison to proceed.
|
||||
|
||||
Additionally, custom types that are part of imported or third party
|
||||
packages which have special behaviors such as per-dialect behavior
|
||||
should implement a method called ``compare_against_backend()``
|
||||
on their SQLAlchemy type. If this method is present, it will be called
|
||||
where it can also return True or False to specify the types compare as
|
||||
equivalent or not; if it returns None, default type comparison logic
|
||||
will proceed::
|
||||
|
||||
class MySpecialType(TypeDecorator):
|
||||
|
||||
# ...
|
||||
|
||||
def compare_against_backend(self, dialect, conn_type):
|
||||
# return True if the types are different,
|
||||
# False if not, or None to allow the default implementation
|
||||
# to compare these types
|
||||
if dialect.name == 'postgresql':
|
||||
return isinstance(conn_type, postgresql.UUID)
|
||||
else:
|
||||
return isinstance(conn_type, String)
|
||||
|
||||
The order of precedence regarding the
|
||||
:paramref:`.EnvironmentContext.configure.compare_type` callable vs. the
|
||||
type itself implementing ``compare_against_backend`` is that the
|
||||
:paramref:`.EnvironmentContext.configure.compare_type` callable is favored
|
||||
first; if it returns ``None``, then the ``compare_against_backend`` method
|
||||
will be used, if present on the metadata type. If that reutrns ``None``,
|
||||
then a basic check for type equivalence is run.
|
||||
|
||||
.. versionadded:: 0.7.6 - added support for the ``compare_against_backend()``
|
||||
method.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,16 @@ Changelog
|
|||
.. changelog::
|
||||
:version: 0.7.6
|
||||
|
||||
.. change::
|
||||
:tags: feature, autogenerate
|
||||
:tickets: 296
|
||||
|
||||
Added support for type comparison functions to be not just per
|
||||
environment, but also present on the custom types themselves, by
|
||||
supplying a method ``compare_against_backend``.
|
||||
Added a new documentation section :ref:`compare_types` describing
|
||||
type comparison fully.
|
||||
|
||||
.. change::
|
||||
:tags: feature, operations
|
||||
:tickets: 255
|
||||
|
|
|
@ -773,6 +773,32 @@ nullable=True))
|
|||
)
|
||||
assert not diff
|
||||
|
||||
def test_custom_type_compare(self):
|
||||
class MyType(TypeDecorator):
|
||||
impl = Integer
|
||||
|
||||
def compare_against_backend(self, dialect, conn_type):
|
||||
return isinstance(conn_type, Integer)
|
||||
|
||||
diff = []
|
||||
autogenerate.compare._compare_type(None, "sometable", "somecol",
|
||||
Column("somecol", INTEGER()),
|
||||
Column("somecol", MyType()),
|
||||
diff, self.autogen_context
|
||||
)
|
||||
assert not diff
|
||||
|
||||
diff = []
|
||||
autogenerate.compare._compare_type(None, "sometable", "somecol",
|
||||
Column("somecol", String()),
|
||||
Column("somecol", MyType()),
|
||||
diff, self.autogen_context
|
||||
)
|
||||
eq_(
|
||||
diff[0][0:4],
|
||||
('modify_type', None, 'sometable', 'somecol')
|
||||
)
|
||||
|
||||
def test_affinity_typedec(self):
|
||||
class MyType(TypeDecorator):
|
||||
impl = CHAR
|
||||
|
|
Loading…
Reference in New Issue