Blog · Oracle
Django cuts table and index names to 30 characters on Oracle
Oracle 12.2 raised the limit for an identifier from 30 bytes to 128. Django keeps the old number. On Oracle 23ai, a fresh Django 6.1 connection still reports 30, and long names get a truncated form.
>>> connection.vendor
'oracle'
>>> connection.ops.max_name_length()
30
The limit applies to every generated name: the table, the columns, the indexes, and the constraints. Django does not raise an error above the limit. It shortens the name and appends a hash of the full name, so that two long names stay distinct.
# app "tests"
class QuarterlyRevenueReconciliationSnapshot(models.Model):
account_reference_identifier = models.CharField(
max_length=20, db_index=True)
>>> QuarterlyRevenueReconciliationSnapshot._meta.db_table
# Oracle: 'tests_quarterlyrevenuerecocd7e' (30)
# PostgreSQL: 'tests_quarterlyrevenuereconciliationsnapshot' (44)
# the index on that column, read from user_indexes:
# TESTS_QUAR_ACCOUNT_RE_3E3EA4D4
The hash keeps the name stable. The same model always produces the same table name, so a migration applies more than one time without a problem.
Note that _meta.db_table holds the short form. Django truncates the name when it
builds the model class, and it asks the default connection for the limit. Thus the value depends on
which database is the default one. A print of it on a PostgreSQL workstation shows the long name.
A migration that names a constraint by hand breaks on Oracle. A
RunSQL that reads DROP INDEX tests_quarterlyrevenuereconciliation_...
finds no such index, because the real name is the truncated form. Before a hand-written
RunSQL reaches Oracle, run manage.py sqlmigrate app 0002 against the
Oracle settings and copy the name from that output. The names differ per backend, so the name
from a PostgreSQL machine is the wrong name.
Read the real names
sqlmigrate shows the SQL for one migration, with the names that this backend
produces. Point the command at the Oracle settings.
manage.py sqlmigrate reports 0002 --settings=config.settings.oracle
To read the names that already exist, query the data dictionary.
SELECT index_name FROM user_indexes
WHERE table_name = 'TESTS_QUARTERLYREVENUERECOCD7E';
SELECT constraint_name FROM user_constraints
WHERE table_name = 'TESTS_QUARTERLYREVENUERECOCD7E';
Oracle stores an unquoted identifier in upper case, and Django quotes its identifiers. Thus the
name in the data dictionary is upper case, and a query against
user_indexes needs the upper-case form or an UPPER() call.
Keep the names short instead
Set db_table for a model with a long name, and set name on an explicit
Index. A name that is short enough on every backend gives one schema everywhere, and
the hash never appears.
class QuarterlyRevenueReconciliationSnapshot(models.Model):
class Meta:
db_table = "reports_qtr_recon"
indexes = [models.Index(fields=["account_reference_identifier"],
name="reports_qtr_recon_acct")]
Measured on Oracle Database 23ai Free and PostgreSQL 17, with Django 6.1 and the
oracledb driver. The 38-character model name above produced a 30-character table
name on Oracle and the full 44-character name on PostgreSQL, where the limit is 63.