diff options
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 9 | ||||
-rw-r--r-- | doc/build/core/constraints.rst | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/schema.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/naming.py | 50 |
4 files changed, 58 insertions, 5 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 20a0b656d..10879e2ca 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,6 +15,15 @@ :version: 0.9.4 .. change:: + :tags: feature, sql + + Added a new feature :func:`.schema.conv`, the purpose of which is to + mark a constraint name as already having had a naming convention applied. + This token will be used by Alembic migrations as of Alembic 0.6.4 + in order to render constraints in migration scripts with names marked + as already having been subject to a naming convention. + + .. change:: :tags: bug, sql :paramref:`.MetaData.naming_convention` feature will now also diff --git a/doc/build/core/constraints.rst b/doc/build/core/constraints.rst index 3979631b0..580fbc0b6 100644 --- a/doc/build/core/constraints.rst +++ b/doc/build/core/constraints.rst @@ -459,6 +459,7 @@ Constraints API :members: :inherited-members: +.. autofunction:: sqlalchemy.schema.conv .. _schema_indexes: diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 9e647e595..8556272a6 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -37,6 +37,9 @@ from .sql.schema import ( ) +from .sql.naming import conv + + from .sql.ddl import ( DDL, CreateTable, diff --git a/lib/sqlalchemy/sql/naming.py b/lib/sqlalchemy/sql/naming.py index 3ba7f5105..1c5fae193 100644 --- a/lib/sqlalchemy/sql/naming.py +++ b/lib/sqlalchemy/sql/naming.py @@ -16,13 +16,54 @@ from .. import exc from .elements import _truncated_label import re +class conv(_truncated_label): + """Mark a string indicating that a name has already been converted + by a naming convention. + + This is a string subclass that indicates a name that should not be + subject to any further naming conventions. + + E.g. when we create a :class:`.Constraint` using a naming convention + as follows:: + + m = MetaData(naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}) + t = Table('t', m, Column('x', Integer), + CheckConstraint('x > 5', name='x5')) + + The name of the above constraint will be rendered as ``"ck_t_x5"``. That is, + the existing name ``x5`` is used in the naming convention as the ``constraint_name`` + token. + + In some situations, such as in migration scripts, we may be rendering + the above :class:`.CheckConstraint` with a name that's already been + converted. In order to make sure the name isn't double-modified, the + new name is applied using the :func:`.schema.conv` marker. We can + use this explicitly as follows:: + + + m = MetaData(naming_convention={"ck": "ck_%(table_name)s_%(constraint_name)s"}) + t = Table('t', m, Column('x', Integer), + CheckConstraint('x > 5', name=conv('ck_t_x5'))) + + Where above, the :func:`.schema.conv` marker indicates that the constraint + name here is final, and the name will render as ``"ck_t_x5"`` and not + ``"ck_t_ck_t_x5"`` + + .. versionadded:: 0.9.4 + + .. seealso:: + + :ref:`constraint_naming_conventions` + + """ + class ConventionDict(object): def __init__(self, const, table, convention): self.const = const self._is_fk = isinstance(const, ForeignKeyConstraint) self.table = table self.convention = convention - self._const_name = const._orig_name = getattr(const, '_orig_name', const.name) + self._const_name = const.name def _key_table_name(self): return self.table.name @@ -41,9 +82,8 @@ class ConventionDict(object): "%(constraint_name)s token requires that " "constraint is explicitly named." ) - # they asked for a name that's derived from the existing - # name, so set the existing name to None - self.const.name = None + if not isinstance(self._const_name, conv): + self.const.name = None return self._const_name def _key_column_X_name(self, idx): @@ -118,7 +158,7 @@ def _constraint_name(const, table): metadata = table.metadata convention = _get_convention(metadata.naming_convention, type(const)) if convention is not None: - newname = _truncated_label( + newname = conv( convention % ConventionDict(const, table, metadata.naming_convention) ) if const.name is None: |