diff options
author | Paul <pbrackin@gmail.com> | 2017-02-20 15:20:17 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-02-21 16:28:51 -0500 |
commit | f1cf86ea6a33a6fa09de4fb727f6383ce6698304 (patch) | |
tree | eecf5d37a5392d1894c0f069d8ba27374d866365 | |
parent | 44d028f42b2a27b8710f2d4d9443e11515814eb4 (diff) | |
download | alembic-f1cf86ea6a33a6fa09de4fb727f6383ce6698304.tar.gz |
Fix postgresql automigration for ARRAY types
Adds a new codepath into render._repr_type() that will consult
the dialect impl for specific types. On the postgresql side,
the exisiting repr() is combined with a replace featuring
the full autogen render of the nested type.
Co-authored-by: Mike Bayer <mike_mp@zzzcomputing.com>
Fixes: #85
Change-Id: I8796bfeea27d48e6f8bb5ea4562bdc04961ba0d5
Pull-request: https://github.com/zzzeek/alembic/pull/38
-rw-r--r-- | alembic/autogenerate/render.py | 9 | ||||
-rw-r--r-- | alembic/ddl/impl.py | 3 | ||||
-rw-r--r-- | alembic/ddl/postgresql.py | 14 | ||||
-rw-r--r-- | docs/build/changelog.rst | 9 | ||||
-rw-r--r-- | tests/test_postgresql.py | 44 |
5 files changed, 76 insertions, 3 deletions
diff --git a/alembic/autogenerate/render.py b/alembic/autogenerate/render.py index 476c1f9..6e10792 100644 --- a/alembic/autogenerate/render.py +++ b/alembic/autogenerate/render.py @@ -566,13 +566,20 @@ def _repr_type(type_, autogen_context): if rendered is not False: return rendered + if hasattr(autogen_context.migration_context, 'impl'): + impl_rt = autogen_context.migration_context.impl.render_type( + type_, autogen_context) + mod = type(type_).__module__ imports = autogen_context.imports if mod.startswith("sqlalchemy.dialects"): dname = re.match(r"sqlalchemy\.dialects\.(\w+)", mod).group(1) if imports is not None: imports.add("from sqlalchemy.dialects import %s" % dname) - return "%s.%r" % (dname, type_) + if impl_rt: + return impl_rt + else: + return "%s.%r" % (dname, type_) elif mod.startswith("sqlalchemy."): prefix = _sqlalchemy_autogenerate_prefix(autogen_context) return "%s%r" % (prefix, type_) diff --git a/alembic/ddl/impl.py b/alembic/ddl/impl.py index 52cc470..0971c21 100644 --- a/alembic/ddl/impl.py +++ b/alembic/ddl/impl.py @@ -320,6 +320,9 @@ class DefaultImpl(with_metaclass(ImplMeta)): """ self.static_output("COMMIT" + self.command_terminator) + def render_type(self, type_obj, autogen_context): + return False + def _string_compare(t1, t2): return \ diff --git a/alembic/ddl/postgresql.py b/alembic/ddl/postgresql.py index fa78e53..ecf0dda 100644 --- a/alembic/ddl/postgresql.py +++ b/alembic/ddl/postgresql.py @@ -6,6 +6,7 @@ from .base import compiles, alter_column, alter_table, format_table_name, \ format_type, AlterColumn, RenameTable from .impl import DefaultImpl from sqlalchemy.dialects.postgresql import INTEGER, BIGINT +from ..autogenerate import render from sqlalchemy import text, Numeric, Column from sqlalchemy import types as sqltypes @@ -105,7 +106,6 @@ class PostgresqlImpl(DefaultImpl): **kw) - def autogen_column_reflect(self, inspector, table, column_info): if column_info.get('default') and \ isinstance(column_info['type'], (INTEGER, BIGINT)): @@ -171,6 +171,18 @@ class PostgresqlImpl(DefaultImpl): ) metadata_indexes.discard(idx) + def render_type(self, type_, autogen_context): + if hasattr(self, '_render_%s_type' % type_.__visit_name__): + meth = getattr(self, '_render_%s_type' % type_.__visit_name__) + return meth(type_, autogen_context) + + return False + + def _render_ARRAY_type(self, type_, autogen_context): + sub_type = render._repr_type(type_.item_type, autogen_context) + outer_type = repr(type_).replace(repr(type_.item_type), sub_type) + return "%s.%s" % ("postgresql", outer_type) + class PostgresqlColumnType(AlterColumn): diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index 2ccdf6c..ca6834d 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -7,6 +7,15 @@ Changelog :version: 0.9.0 :released: + .. change:: 85 + :tags: bug, postgresql + :tickets: 85 + + Fixed bug where Postgresql ARRAY type would not render the import prefix + for the inner type; additionally, user-defined renderers take place + for the inner type as well as the outer type. Pull request courtesy + Paul Brackin. + .. change:: fk_schema_compare :tags: bug, operations diff --git a/tests/test_postgresql.py b/tests/test_postgresql.py index 2a3378d..628357b 100644 --- a/tests/test_postgresql.py +++ b/tests/test_postgresql.py @@ -1,7 +1,7 @@ from sqlalchemy import DateTime, MetaData, Table, Column, text, Integer, \ String, Interval, Sequence, Numeric, BigInteger, Float, Numeric -from sqlalchemy.dialects.postgresql import ARRAY, UUID +from sqlalchemy.dialects.postgresql import ARRAY, UUID, BYTEA from sqlalchemy.engine.reflection import Inspector from alembic.operations import Operations from sqlalchemy.sql import table, column @@ -602,3 +602,45 @@ unique=False, """ 'nullable=False)' ) + @config.requirements.sqlalchemy_09 + def test_array_type(self): + + eq_ignore_whitespace( + autogenerate.render._repr_type( + ARRAY(Integer), self.autogen_context), + "postgresql.ARRAY(sa.Integer())" + ) + + eq_ignore_whitespace( + autogenerate.render._repr_type( + ARRAY(DateTime(timezone=True)), self.autogen_context), + "postgresql.ARRAY(sa.DateTime(timezone=True))" + ) + + eq_ignore_whitespace( + autogenerate.render._repr_type( + ARRAY(BYTEA, as_tuple=True, dimensions=2), + self.autogen_context), + "postgresql.ARRAY(postgresql.BYTEA(), as_tuple=True, dimensions=2)" + ) + + assert 'from sqlalchemy.dialects import postgresql' in \ + self.autogen_context.imports + + @config.requirements.sqlalchemy_09 + def test_array_type_user_defined_inner(self): + def repr_type(typestring, object_, autogen_context): + if typestring == 'type' and isinstance(object_, String): + return "foobar.MYVARCHAR" + else: + return False + + self.autogen_context.opts.update( + render_item=repr_type + ) + + eq_ignore_whitespace( + autogenerate.render._repr_type( + ARRAY(String), self.autogen_context), + "postgresql.ARRAY(foobar.MYVARCHAR)" + ) |