:paramref:`.EnvironmentContext.configure.user_module_prefix`.
This prefix is applied when autogenerate renders a user-defined type,
which here is defined as any type that is from a module outside of the
``sqlalchemy.`` hierarchy. This prefix defaults to ``None``, in
which case the :paramref:`.EnvironmentContext.configure.sqlalchemy_module_prefix`
is used, thus preserving the current behavior. #171
- added new documentation sections regarding rendering of types
- improved CSS so that deprecations/version changes are highlighted
- cleanup of some configure paramter docs
existing_nullable=None,
existing_server_default=False,
schema=None):
- sqla_prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
indent = " " * 11
text = "%(prefix)salter_column(%(tname)r, %(cname)r" % {
'prefix': _alembic_autogenerate_prefix(
'tname': tname,
'cname': cname}
text += ",\n%sexisting_type=%s" % (indent,
- _repr_type(sqla_prefix, existing_type, autogen_context))
+ _repr_type(existing_type, autogen_context))
if server_default is not False:
rendered = _render_server_default(
server_default, autogen_context)
if type_ is not None:
text += ",\n%stype_=%s" % (indent,
- _repr_type(sqla_prefix, type_, autogen_context))
+ _repr_type(type_, autogen_context))
if nullable is not None:
text += ",\n%snullable=%r" % (
indent, nullable,)
text += ")"
return text
+def _user_autogenerate_prefix(autogen_context):
+ prefix = autogen_context['opts']['user_module_prefix']
+ if prefix is None:
+ return _sqlalchemy_autogenerate_prefix(autogen_context)
+ else:
+ return prefix
+
def _sqlalchemy_autogenerate_prefix(autogen_context):
return autogen_context['opts']['sqlalchemy_module_prefix'] or ''
return "%(prefix)sColumn(%(name)r, %(type)s, %(kw)s)" % {
'prefix': _sqlalchemy_autogenerate_prefix(autogen_context),
'name': column.name,
- 'type': _repr_type(_sqlalchemy_autogenerate_prefix(autogen_context),
- column.type, autogen_context),
+ 'type': _repr_type(column.type, autogen_context),
'kw': ", ".join(["%s=%s" % (kwname, val) for kwname, val in opts])
}
else:
return None
-def _repr_type(prefix, type_, autogen_context):
+def _repr_type(type_, autogen_context):
rendered = _user_defined_render("type", type_, autogen_context)
if rendered is not False:
return rendered
if imports is not None:
imports.add("from sqlalchemy.dialects import %s" % dname)
return "%s.%r" % (dname, type_)
+ elif mod.startswith("sqlalchemy"):
+ prefix = _sqlalchemy_autogenerate_prefix(autogen_context)
+ return "%s%r" % (prefix, type_)
else:
+ prefix = _user_autogenerate_prefix(autogen_context)
return "%s%r" % (prefix, type_)
def _render_constraint(constraint, autogen_context):
downgrade_token="downgrades",
alembic_module_prefix="op.",
sqlalchemy_module_prefix="sa.",
+ user_module_prefix=None,
**kw
):
"""Configure a :class:`.MigrationContext` within this
.. seealso::
- ``include_schemas``, ``include_symbol``
+ :ref:`autogen_render_types`
+ :paramref:`.EnvironmentContext.configure.include_schemas`
:param include_symbol: A callable function which, given a table name
and schema name (may be ``None``), returns ``True`` or ``False``, indicating
if the given table should be considered in the autogenerate sweep.
- .. deprecated:: 0.6.0 ``include_symbol`` is superceded by the
- more generic ``include_object`` parameter.
+ .. deprecated:: 0.6.0 :paramref:`.EnvironmentContext.configure.include_symbol`
+ is superceded by the more generic
+ :paramref:`.EnvironmentContext.configure.include_object`
+ parameter.
E.g.::
include_symbol = include_symbol
)
- To limit autogenerate to a certain set of schemas when using the
- ``include_schemas`` option::
-
- def include_symbol(tablename, schema):
- return schema in (None, "schema1", "schema2")
-
- context.configure(
- # ...
- include_schemas = True,
- include_symbol = include_symbol
- )
-
- .. versionadded:: 0.3.6
-
- .. versionchanged:: 0.4.0 the ``include_symbol`` callable must now
- also accept a "schema" argument, which may be None.
-
.. seealso::
- ``include_schemas``, ``include_object``
+ :paramref:`.EnvironmentContext.configure.include_schemas`
+
+ :paramref:`.EnvironmentContext.configure.include_object`
:param include_schemas: If True, autogenerate will scan across
all schemas located by the SQLAlchemy
.. seealso::
- ``include_symbol``, ``include_object``
+ :paramref:`.EnvironmentContext.configure.include_object`
:param render_item: Callable that can be used to override how
any schema item, i.e. column, constraint, type,
will render them using the dialect module name, i.e. ``mssql.BIT()``,
``postgresql.UUID()``.
+ :param user_module_prefix: When autogenerate refers to a SQLAlchemy
+ type (e.g. :class:`.TypeEngine`) where the module name is not
+ under the ``sqlalchemy`` namespace, this prefix will be used
+ within autogenerate, if non-``None``; if left at its default of
+ ``None``, the
+ :paramref:`.EnvironmentContext.configure.sqlalchemy_module_prefix`
+ is used instead.
+
+ .. versionadded:: 0.6.3 added
+ :paramref:`.EnvironmentContext.configure.user_module_prefix`
+
+ .. seealso::
+
+ :ref:`autogen_module_prefix`
Parameters specific to individual backends:
opts['downgrade_token'] = downgrade_token
opts['sqlalchemy_module_prefix'] = sqlalchemy_module_prefix
opts['alembic_module_prefix'] = alembic_module_prefix
+ opts['user_module_prefix'] = user_module_prefix
if render_item is not None:
opts['render_item'] = render_item
if compare_type is not None:
--- /dev/null
+@import url("nature.css");
+
+
+.versionadded, .versionchanged, .deprecated {
+ background-color: #FFFFCC;
+ border: 1px solid #FFFF66;
+ margin-bottom: 10px;
+ margin-top: 10px;
+ padding: 7px;
+}
+
+.versionadded > p > span, .versionchanged > p > span, .deprecated > p > span{
+ font-style: italic;
+}
.. changelog::
:version: 0.6.3
+ .. change::
+ :tags: feature
+ :tickets: 171
+
+ Added new argument
+ :paramref:`.EnvironmentContext.configure.user_module_prefix`.
+ This prefix is applied when autogenerate renders a user-defined type,
+ which here is defined as any type that is from a module outside of the
+ ``sqlalchemy.`` hierarchy. This prefix defaults to ``None``, in
+ which case the :paramref:`.EnvironmentContext.configure.sqlalchemy_module_prefix`
+ is used, thus preserving the current behavior.
+
.. change::
:tags: bug
:tickets: 170
# Sphinx are currently 'default' and 'sphinxdoc'.
html_theme = 'nature'
+html_style = "nature_override.css"
+
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
like CHECK and FOREIGN KEY - these are not fully implemented.
* Sequence additions, removals - not yet implemented.
+.. _autogen_render_types:
+
Rendering Custom Types in Autogenerate
--------------------------------------
faithfully reproduce the constructor for that type. But beyond that, it
also is usually necessary to change how the enclosing module or package
is rendered as well;
-this is accomplished using the ``render_item`` configuration option::
+this is accomplished using the :paramref:`.EnvironmentContext.configure.render_item`
+configuration option::
def render_item(type_, obj, autogen_context):
"""Apply custom rendering for selected items."""
Above, we also need to make sure our ``MySpecialType`` includes an appropriate
``__repr__()`` method, which is invoked when we call it against ``"%r"``.
+The callable we use for :paramref:`.EnvironmentContext.configure.render_item`
+can also add imports to our migration script. The ``autogen_context`` passed in
+contains an entry called ``autogen_context['imports']``, which is a Python
+``set()`` for which we can add new imports. For example, if ``MySpecialType``
+were in a module called ``mymodel.types``, we can add the import for it
+as we encounter the type::
+
+ def render_item(type_, obj, autogen_context):
+ """Apply custom rendering for selected items."""
+
+ if type_ == 'type' and isinstance(obj, MySpecialType):
+ # add import for this type
+ autogen_context['imports'].add("from mymodel import types")
+ return "types.%r" % obj
+
+ # default rendering for other objects
+ return False
+
+The finished migration script will include our imports where the
+``${imports}`` expression is used, producing output such as::
+
+ from alembic import op
+ import sqlalchemy as sa
+ from mymodel import types
+
+ def upgrade():
+ op.add_column('sometable', Column('mycolumn', types.MySpecialType()))
+
+.. _autogen_module_prefix:
+
+Controlling the Module Prefix
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When using :paramref:`.EnvironmentContext.configure.render_item`, note that
+we deliver not just the reproduction of the type, but we can also deliver the
+"module prefix", which is a module namespace from which our type can be found
+within our migration script. When Alembic renders SQLAlchemy types, it will
+typically use the value of :paramref:`.EnvironmentContext.configure.sqlalchemy_module_prefix`,
+which defaults to ``"sa."``, to achieve this::
+
+ Column("my_column", sa.Integer())
+
+When we use a custom type that is not within the ``sqlalchemy.`` module namespace,
+by default Alembic will still use the ``"sa."`` prefix::
+
+ Column("my_column", sa.MyCustomType())
+
+We can provide an alternate prefix here using the :paramref:`.EnvironmentContext.configure.user_module_prefix`
+option::
+
+
+ def run_migrations_online():
+ # ...
+
+ context.configure(
+ connection=connection,
+ target_metadata=target_metadata,
+ user_module_prefix="mymodel.types",
+ # ...
+ )
+
+ # ...
+
+Where we'd get a migration like::
+
+ Column("my_column", mymodel.types.MyCustomType())
+
+.. versionadded:: 0.6.3 Added :paramref:`.EnvironmentContext.configure.user_module_prefix`.
+
Generating SQL Scripts (a.k.a. "Offline Mode")
==============================================
autogenerate.render._drop_index(idx, self.autogen_context),
"op.drop_index('test_active_code_idx', table_name='test')"
)
-
+
def test_drop_index_schema(self):
"""
autogenerate.render._drop_index using schema
autogenerate.render._add_unique_constraint(uq, self.autogen_context),
"op.create_unique_constraint('uq_test_code', 'test', ['code'])"
)
-
+
def test_add_unique_constraint_schema(self):
"""
autogenerate.render._add_unique_constraint using schema
autogenerate.render._drop_constraint(uq, self.autogen_context),
"op.drop_constraint('uq_test_code', 'test')"
)
-
+
def test_drop_constraint_schema(self):
"""
autogenerate.render._drop_constraint using schema
def test_render_enum(self):
eq_ignore_whitespace(
autogenerate.render._repr_type(
- "sa.",
Enum("one", "two", "three", name="myenum"),
self.autogen_context),
"sa.Enum('one', 'two', 'three', name='myenum')"
)
eq_ignore_whitespace(
autogenerate.render._repr_type(
- "sa.",
Enum("one", "two", "three"),
self.autogen_context),
"sa.Enum('one', 'two', 'three')"
)
-# TODO: tests for dialect-specific type rendering + imports
+ def test_repr_plain_sqla_type(self):
+ type_ = Integer()
+ autogen_context = {
+ 'opts': {
+ 'sqlalchemy_module_prefix': 'sa.',
+ 'alembic_module_prefix': 'op.',
+ },
+ 'dialect': mysql.dialect()
+ }
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(type_, autogen_context),
+ "sa.Integer()"
+ )
+
+ def test_repr_user_type_user_prefix_None(self):
+ from sqlalchemy.types import UserDefinedType
+ class MyType(UserDefinedType):
+ def get_col_spec(self):
+ return "MYTYPE"
+
+ type_ = MyType()
+ autogen_context = {
+ 'opts': {
+ 'sqlalchemy_module_prefix': 'sa.',
+ 'alembic_module_prefix': 'op.',
+ 'user_module_prefix': None
+ },
+ 'dialect': mysql.dialect()
+ }
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(type_, autogen_context),
+ "sa.MyType()"
+ )
+
+ def test_repr_user_type_user_prefix_present(self):
+ from sqlalchemy.types import UserDefinedType
+ class MyType(UserDefinedType):
+ def get_col_spec(self):
+ return "MYTYPE"
+
+ type_ = MyType()
+ autogen_context = {
+ 'opts': {
+ 'sqlalchemy_module_prefix': 'sa.',
+ 'alembic_module_prefix': 'op.',
+ 'user_module_prefix': 'user.',
+ },
+ 'dialect': mysql.dialect()
+ }
+
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(type_, autogen_context),
+ "user.MyType()"
+ )
+
+ def test_repr_dialect_type(self):
+ from sqlalchemy.dialects.mysql import VARCHAR
+
+ type_ = VARCHAR(20, charset='utf8', national=True)
+ autogen_context = {
+ 'opts': {
+ 'sqlalchemy_module_prefix': 'sa.',
+ 'alembic_module_prefix': 'op.',
+ 'user_module_prefix': None,
+ },
+ 'imports': set(),
+ 'dialect': mysql.dialect()
+ }
+ eq_ignore_whitespace(
+ autogenerate.render._repr_type(type_, autogen_context),
+ "mysql.VARCHAR(charset='utf8', national=True, length=20)"
+ )
+ eq_(autogen_context['imports'],
+ set(['from sqlalchemy.dialects import mysql'])
+ )