<p dir="auto"><strong>Migrated issue, originally created by Adrian (<a href="https://github.com/thiefmaster">@thiefmaster</a>)</strong></p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base


def _column_names(constraint, table):
    return '_'.join((c if isinstance(c, basestring) else c.name) for c in constraint.columns)


naming_convention = {
    'fk': 'fk_%(table_name)s_%(column_names)s_%(referred_table_name)s',
    'ix': 'ix_%(table_name)s_%(column_names)s',
    'column_names': _column_names,
}


Base = declarative_base()
Base.metadata.naming_convention = naming_convention


class A(Base):
    __tablename__ = 'test_very_long_name_abcdefghijklmnopqrstuvwxyz'
    __table_args__ = Index(None, 'abcdefghijklmnopqrstuvwxyz'),

    id = Column(Integer, primary_key=True)
    abcdefghijklmnopqrstuvwxyz = Column(Integer)


class B(Base):
    __tablename__ = 'test_very_long_name_abcdefghijklmnopqrstuvwxyz_b'

    id = Column(Integer, primary_key=True)
    abcdefghijklmnopqrstuvwxyz = Column(Integer, ForeignKey('test_very_long_name_abcdefghijklmnopqrstuvwxyz.id'))


e = create_engine('postgresql:///test', echo=True)
Base.metadata.create_all(e)
raw_input('check the names...')
e.execute('DROP TABLE test_very_long_name_abcdefghijklmnopqrstuvwxyz, test_very_long_name_abcdefghijklmnopqrstuvwxyz_b')"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span> <span class="pl-k">import</span> <span class="pl-c1">*</span>
<span class="pl-k">from</span> <span class="pl-s1">sqlalchemy</span>.<span class="pl-s1">ext</span>.<span class="pl-s1">declarative</span> <span class="pl-k">import</span> <span class="pl-s1">declarative_base</span>


<span class="pl-k">def</span> <span class="pl-en">_column_names</span>(<span class="pl-s1">constraint</span>, <span class="pl-s1">table</span>):
    <span class="pl-k">return</span> <span class="pl-s">'_'</span>.<span class="pl-en">join</span>((<span class="pl-s1">c</span> <span class="pl-k">if</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">c</span>, <span class="pl-s1">basestring</span>) <span class="pl-k">else</span> <span class="pl-s1">c</span>.<span class="pl-s1">name</span>) <span class="pl-k">for</span> <span class="pl-s1">c</span> <span class="pl-c1">in</span> <span class="pl-s1">constraint</span>.<span class="pl-s1">columns</span>)


<span class="pl-s1">naming_convention</span> <span class="pl-c1">=</span> {
    <span class="pl-s">'fk'</span>: <span class="pl-s">'fk_%(table_name)s_%(column_names)s_%(referred_table_name)s'</span>,
    <span class="pl-s">'ix'</span>: <span class="pl-s">'ix_%(table_name)s_%(column_names)s'</span>,
    <span class="pl-s">'column_names'</span>: <span class="pl-s1">_column_names</span>,
}


<span class="pl-v">Base</span> <span class="pl-c1">=</span> <span class="pl-en">declarative_base</span>()
<span class="pl-v">Base</span>.<span class="pl-s1">metadata</span>.<span class="pl-s1">naming_convention</span> <span class="pl-c1">=</span> <span class="pl-s1">naming_convention</span>


<span class="pl-k">class</span> <span class="pl-v">A</span>(<span class="pl-v">Base</span>):
    <span class="pl-s1">__tablename__</span> <span class="pl-c1">=</span> <span class="pl-s">'test_very_long_name_abcdefghijklmnopqrstuvwxyz'</span>
    <span class="pl-s1">__table_args__</span> <span class="pl-c1">=</span> <span class="pl-v">Index</span>(<span class="pl-c1">None</span>, <span class="pl-s">'abcdefghijklmnopqrstuvwxyz'</span>),

    <span class="pl-s1">id</span> <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">Integer</span>, <span class="pl-s1">primary_key</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)
    <span class="pl-s1">abcdefghijklmnopqrstuvwxyz</span> <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">Integer</span>)


<span class="pl-k">class</span> <span class="pl-v">B</span>(<span class="pl-v">Base</span>):
    <span class="pl-s1">__tablename__</span> <span class="pl-c1">=</span> <span class="pl-s">'test_very_long_name_abcdefghijklmnopqrstuvwxyz_b'</span>

    <span class="pl-s1">id</span> <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">Integer</span>, <span class="pl-s1">primary_key</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)
    <span class="pl-s1">abcdefghijklmnopqrstuvwxyz</span> <span class="pl-c1">=</span> <span class="pl-v">Column</span>(<span class="pl-v">Integer</span>, <span class="pl-v">ForeignKey</span>(<span class="pl-s">'test_very_long_name_abcdefghijklmnopqrstuvwxyz.id'</span>))


<span class="pl-s1">e</span> <span class="pl-c1">=</span> <span class="pl-en">create_engine</span>(<span class="pl-s">'postgresql:///test'</span>, <span class="pl-s1">echo</span><span class="pl-c1">=</span><span class="pl-c1">True</span>)
<span class="pl-v">Base</span>.<span class="pl-s1">metadata</span>.<span class="pl-en">create_all</span>(<span class="pl-s1">e</span>)
<span class="pl-en">raw_input</span>(<span class="pl-s">'check the names...'</span>)
<span class="pl-s1">e</span>.<span class="pl-en">execute</span>(<span class="pl-s">'DROP TABLE test_very_long_name_abcdefghijklmnopqrstuvwxyz, test_very_long_name_abcdefghijklmnopqrstuvwxyz_b'</span>)</pre></div>
<p dir="auto">This results in the following SQL (removed unrelated parts):</p>
<div class="highlight highlight-source-sql notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="CREATE INDEX ix_test_very_long_name_abcdefghijklmnopqrstuvwxyz_abcde_5f65 ON test_very_long_name_abcdefghijklmnopqrstuvwxyz (abcdefghijklmnopqrstuvwxyz)

CREATE TABLE test_very_long_name_abcdefghijklmnopqrstuvwxyz_b (
        ...
        CONSTRAINT fk_test_very_long_name_abcdefghijklmnopqrstuvwxyz_b_abcdefghijklmnopqrstuvwxyz_test_very_long_name_abcdefghijklmnopqrstuvwxyz FOREIGN KEY(abcdefghijklmnopqrstuvwxyz) REFERENCES test_very_long_name_abcdefghijklmnopqrstuvwxyz (id)
)"><pre class="notranslate"><span class="pl-k">CREATE</span> <span class="pl-k">INDEX</span> <span class="pl-en">ix_test_very_long_name_abcdefghijklmnopqrstuvwxyz_abcde_5f65</span> <span class="pl-k">ON</span> test_very_long_name_abcdefghijklmnopqrstuvwxyz (abcdefghijklmnopqrstuvwxyz)

<span class="pl-k">CREATE</span> <span class="pl-k">TABLE</span> <span class="pl-en">test_very_long_name_abcdefghijklmnopqrstuvwxyz_b</span> (
        ...
        <span class="pl-k">CONSTRAINT</span> fk_test_very_long_name_abcdefghijklmnopqrstuvwxyz_b_abcdefghijklmnopqrstuvwxyz_test_very_long_name_abcdefghijklmnopqrstuvwxyz <span class="pl-k">FOREIGN KEY</span>(abcdefghijklmnopqrstuvwxyz) <span class="pl-k">REFERENCES</span> test_very_long_name_abcdefghijklmnopqrstuvwxyz (id)
)</pre></div>
<p dir="auto">SQLAlchemy properly truncates the index name and appends a hash for uniqueness, but the constraint name is left unmodified, resulting in Postgres truncating it to 63 characters (without adding a hash to the end to ensure uniqueness).</p>
<p dir="auto">I think this behavior is quite inconsistent and may even result in problems depending on the structure since it may result in two truncated names being the same even though they were different before. It's also somewhat ugly in Alembic migration scripts as you'll have to use <code class="notranslate">op.f('...')</code> for the index to ensure it's truncated by SA while it's optional for FK names since they are not touched even if too long.</p>
<p dir="auto"><strong>Migrated issue, originally created by whereswalden</strong></p>
<p dir="auto">Currently, <a href="http://docs.sqlalchemy.org/en/latest/core/metadata.html#sqlalchemy.schema.MetaData.params.naming_convention" rel="nofollow">MetaData.naming_convention</a> does not support composite indexes or foreign keys, so those must be named explicitly. It'd be nice to be able to provide a template for those to be automatically named as well.</p>
<p dir="auto">It would likely be necessary for users to define a function to provide the name, since the constraint/index could include any number of columns, but a fairly simple example could be provided in the documentation that simply <code class="notranslate">','.join()</code>s the column names.</p>