Skip to content

05. Constraint Naming Convention

Overview

A constraint naming convention defines a consistent and predictable pattern for naming database constraints and indexes. This can applied for such as primary keys, foreign keys, unique constraints, indexes, and check constraints.

This pattern is strongly recommended from the beginning when starting with databases or schema migrations. It helps ensure constraints can be reliably referenced later, especially when working with migration tools (for example, Alembic), refactoring schemas, or supporting multiple database engines.

In SQLAlchemy, the naming convention is configured via the MetaData object using the naming_convention argument. Once defined, it is applied automatically during table creation.

This approach works consistently across databases such as MySQL and PostgreSQL.

Key benefits:

  • Predictable and readable constraint names

  • Easier schema migrations and diffs

  • Better cross-database compatibility

  • Avoids unnamed or randomly named constraints

Property Value
Object MetaData
Argument naming_convention
Scope Global
Affects All constraints and indexes
Override Allowed per constraint

Components

Execution Flow

This section describes how constraint naming conventions are configured and applied during schema-related operations:

Step 1: Define a naming convention dictionary that describes how constraints and indexes should be named.

Step 2: Attach the naming convention to the MetaData object using the naming_convention argument.

Step 3: Define tables, columns, and constraints that reference the configured MetaData.

Step 4: Trigger a schema operation that emits DDL (for example, CREATE, ALTER, or migration execution).

Step 5: SQLAlchemy resolves and applies constraint and index names based on the naming convention

Common Constraint Naming Rules

This section defines how constraint and index names are generated using templates configured on MetaData.naming_convention. Each template is a string pattern that may include predefined tokens provided by SQLAlchemy or custom tokens supplied by the user. These tokens are resolved at DDL generation time to produce deterministic constraint and index names.

The table below shows common constraint types and example templates:

Constraint Type Key Template
Primary Key pk pk_%(table_name)s
Foreign Key fk fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s
Unique Constraint uq uq_%(table_name)s_%(column_0_name)s
Check Constraint ck ck_%(table_name)s_%(constraint_name)s
Index ix ix_%(table_name)s_%(column_0_name)s

The following tokens can be used inside templates. Availability depends on the constraint type.

Token Description Applies To
%(table_name)s Name of the associated Table All constraints
%(referred_table_name)s Name of the referenced table Foreign key
%(column_0_name)s Name of the first column Multi-column constraints
%(column_0N_name)s All column names joined without a separator Multi-column constraints
%(column_0_N_name)s All column names joined with underscores Multi-column constraints
%(column_0_label)s Label of the first column Column-based constraints
%(column_0N_label)s All column labels joined without a separator Column-based constraints
%(column_0_N_label)s All column labels joined with underscores Column-based constraints
%(column_0_key)s Key of the first column Column-based constraints
%(column_0N_key)s All column keys joined without a separator Column-based constraints
%(column_0_N_key)s All column keys joined with underscores Column-based constraints
%(referred_column_0_name)s Name of the first referenced column Foreign key
%(referred_column_0N_name)s All referenced column names joined without a separator Foreign key
%(referred_column_0_N_name)s All referenced column names joined with underscores Foreign key
%(referred_column_0_key)s Key of the first referenced column Foreign key
%(referred_column_0N_key)s All referenced column keys joined without a separator Foreign key
%(constraint_name)s Explicitly provided constraint name All constraints
User-Defined Tokens

In addition to built-in tokens, user-defined tokens can be registered by supplying a callable with the signature fn(constraint, table) in the naming_convention dictionary. These custom tokens allow full control over how constraint names are generated.

Generated Names Exceeding Identifier Limit

If a generated constraint or index name exceeds the database identifier limit (commonly 63 characters), SQLAlchemy will silently truncate the name without raising an error or affecting constraint behavior; explicitly naming a constraint or index will override the configured naming convention.

Example Use-Cases

General Case

A common and practical naming convention for most applications is shown below. It covers indexes, unique constraints, checks, foreign keys, and primary keys.

If models or tables are defined using this MetaData instance, all constraints and indexes will automatically receive names based on the convention.

from sqlalchemy import MetaData

convention = {
    "ix": "ix_%(column_0_label)s",
    "uq": "uq_%(table_name)s_%(column_0_name)s",
    "ck": "ck_%(table_name)s_%(constraint_name)s",
    "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
    "pk": "pk_%(table_name)s",
}

metadata = MetaData(naming_convention=convention)

Custom Key and Advanced Convention

In some cases, additional logic is required for constraint names. SQLAlchemy allows custom tokens by providing a callable function.

Lets say we want to generate a custom foreign key name using a UUID. fk_guid is a user-defined token

import uuid


def fk_guid(constraint, table):
    str_tokens = (
        [table.name]
        + [element.parent.name for element in constraint.elements]
        + [element.target_fullname for element in constraint.elements]
    )
    guid = uuid.uuid5(
        uuid.NAMESPACE_OID,
        "_".join(str_tokens).encode("ascii"),
    )
    return str(guid)

Then we can use this custom token in the naming convention dictionary.

from sqlalchemy import MetaData

convention = {
    "fk_guid": fk_guid,
    "ix": "ix_%(column_0_label)s",
    "fk": "fk_%(fk_guid)s",
}

metadata = MetaData(naming_convention=convention)

Reference