Alembic generates redundant foreign key operations for existing cross-schema references in PostgreSQL #1676
-
Describe the bug Expected behavior To Reproduce # SQLAlchemy models with cross-schema foreign keys
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class TableA(db.Model):
__tablename__ = 'table_a'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True)
table_b_id = db.Column(db.Integer, db.ForeignKey('schema_b.table_b.id'))
table_c_id = db.Column(db.Integer, db.ForeignKey('public.table_c.id'))
class TableB(db.Model):
__tablename__ = 'table_b'
__table_args__ = {"schema": "schema_b"}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
class TableC(db.Model):
__tablename__ = 'table_c'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100)) # migrations/env.py configuration
def run_migrations_online():
conf_args = current_app.extensions["migrate"].configure_args
# Multi-schema configuration
conf_args["include_schemas"] = True
conf_args["version_table_schema"] = "public"
conf_args["compare_type"] = True
conf_args["compare_server_default"] = True
connectable = get_engine()
with connectable.connect() as connection:
context.configure(connection=connection, target_metadata=get_metadata(), **conf_args)
with context.begin_transaction():
context.run_migrations() Steps to reproduce:
Error # Output from flask db migrate
INFO [alembic.autogenerate.compare] Detected removed foreign key (table_b_id)(id) on table table_a
INFO [alembic.autogenerate.compare] Detected added foreign key (table_b_id)(id) on table public.table_a
INFO [alembic.autogenerate.compare] Detected removed foreign key (table_c_id)(id) on table table_a
INFO [alembic.autogenerate.compare] Detected added foreign key (table_c_id)(id) on table public.table_a
# ... hundreds more similar operations for every foreign key in the database Generated migration contains redundant operations like: def upgrade():
# Drop foreign key that already exists correctly
op.drop_constraint('table_a_table_b_id_fkey', 'table_a', schema='public', type_='foreignkey')
# Recreate identical foreign key with explicit schema reference
op.create_foreign_key('table_a_table_b_id_fkey', 'table_a', 'table_b',
['table_b_id'], ['id'],
source_schema='public', referent_schema='schema_b')
# Repeated for hundreds of foreign keys... Versions.
Additional context Root Cause Analysis: Database State Verification: # Database inspection confirms proper cross-schema references
inspector.get_foreign_keys('table_a', schema='public')
# Returns: [{'name': 'table_a_table_b_id_fkey', 'referred_schema': 'schema_b', 'referred_table': 'table_b', ...}] Extensive Troubleshooting Attempted:
Impact:
Current Workaround: Have a nice day! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi - there's a lot of text here but the first thing I see in the code is something that likely needs to change: class TableA(db.Model):
__tablename__ = 'table_a'
__table_args__ = {"schema": "public"}
id = db.Column(db.Integer, primary_key=True)
table_b_id = db.Column(db.Integer, db.ForeignKey('schema_b.table_b.id'))
table_c_id = db.Column(db.Integer, db.ForeignKey('public.table_c.id')) PostgreSQL database defines the default schema search path as essentially "public", meaning the name "public" is known in SQLAlchemy as the default schema. It will get confused if you name this schema explicitly in your models, so you should remove the name "public" here. See the following sections for guidance and best practices: |
Beta Was this translation helpful? Give feedback.
Hi -
there's a lot of text here but the first thing I see in the code is something that likely needs to change:
PostgreSQL database defines the default schema search path as essentially "public", meaning the name "public" is known in SQLAlchemy as the default schema. It will get confused if you name this schema explicitly in your models, so you should remove the name "public" here.
See the fo…