﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
29345	Migrations that recreate constraints can fail on PostgreSQL if table is not in public schema	Olav Morken	nobody	"The `django.db.backends.postgresql.introspection.get_constraints(...)`-function contains an SQL expression that assumes that all tables are in the `public` schema:
https://github.com/django/django/blob/2.0.4/django/db/backends/postgresql/introspection.py#L178-L201

The last few lines read:

{{{
            JOIN pg_class AS cl ON c.conrelid = cl.oid
            JOIN pg_namespace AS ns ON cl.relnamespace = ns.oid
            WHERE ns.nspname = %s AND cl.relname = %s
        """""", [""public"", table_name])
}}}

The result is that it fails to find any constraints for tables that are not in the `public` schema. This either leaves us with two identical constraints, when it fails to delete the old, or results in an exception when it subsequently tries to recreate a constraint that it should have deleted:

{{{
django.db.utils.ProgrammingError: constraint ""migration_app_testref_test_id_bce0807a_fk"" for relation ""migration_app_testref"" already exists
}}}

A simple fix is to not check for the `public` schema, but instead check visibility using `pg_catalog.pg_table_is_visible(cl.oid)`:

{{{
            JOIN pg_class AS cl ON c.conrelid = cl.oid
            WHERE cl.relname = %s AND pg_catalog.pg_table_is_visible(cl.oid)
        """""", [""public"", table_name])
}}}

This appears to give the correct result, even when there are multiple tables with the same name in the database.

I have attached a migration file and models file for a simple app `migration_app` that reproduces this problem. To be able to reproduce it, you must use a custom schema search path when connecting to PostgreSQL, either by setting it as the default for the role, or by specifying it in the connection options:

{{{
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'migration_test',
        'HOST': 'localhost',
        'USER': 'postgres',
        'OPTIONS': {
            'options': '-c search_path=testschema,public',
        },
    }
}
}}}
"	Bug	closed	Migrations	2.0	Normal	duplicate			Accepted	0	0	0	0	0	0
