Repository: mathesar-foundation/mathesar

Issue:
Moving Foreign key column throws up an error. 
## Description
I'm not able to extract a set of columns where one column is a FK. Specifically, this is step 18 on our library workflow. I get an unhandled exception:

> Foreign key associated with column 'Publications.Authors_id' could not find table 'Library.Authors' with which to generate a foreign key to target column 'id'




Discussion:
This happens due to incomplete metadata when copying 
 foreign key constraint in line https://github.com/centerofci/mathesar/blob/86a4949331df5f577d245eb4f9c751f04f6349c9/db/columns/base.py#L63

Resolving patch:
diff --git a/db/tables/operations/create.py b/db/tables/operations/create.py
--- a/db/tables/operations/create.py
+++ b/db/tables/operations/create.py
@@ -19,6 +19,11 @@ def create_mathesar_table(name, schema, columns, engine, metadata=None):
     # SQLAlchemy context (e.g., for creating a ForeignKey relationship)
     if metadata is None:
         metadata = MetaData(bind=engine, schema=schema)
+    metadata.reflect()
+    # The exception raised by SQLAlchemy upon hitting a duplicate table in the
+    # schema is non-specific.
+    if (name, schema) in [(t.name, t.schema) for t in metadata.sorted_tables]:
+        raise DuplicateTable
     table = Table(
         name,
         metadata,
@@ -29,6 +34,10 @@ def create_mathesar_table(name, schema, columns, engine, metadata=None):
     return table
 
 
+class DuplicateTable(Exception):
+    pass
+
+
 def create_string_column_table(name, schema, column_names, engine):
     """
     This method creates a Postgres table in the specified schema, with all
diff --git a/mathesar/api/serializers/tables.py b/mathesar/api/serializers/tables.py
--- a/mathesar/api/serializers/tables.py
+++ b/mathesar/api/serializers/tables.py
@@ -1,10 +1,10 @@
 from django.urls import reverse
-from psycopg2.errors import DuplicateTable
 from rest_framework import serializers, status
 from rest_framework.exceptions import ValidationError
 from sqlalchemy.exc import ProgrammingError
 
 from db.types.operations.convert import get_db_type_enum_from_id
+from db.tables.operations.create import DuplicateTable
 
 from mathesar.api.exceptions.validation_exceptions.exceptions import (
     ColumnSizeMismatchAPIException, DistinctColumnRequiredAPIException,
@@ -111,16 +111,15 @@ def create(self, validated_data):
                     table.save()
             else:
                 table = create_empty_table(name, schema)
+        except DuplicateTable as e:
+            raise DuplicateTableAPIException(
+                e,
+                message=f"Relation {validated_data['name']} already exists in schema {schema.id}",
+                field="name",
+                status_code=status.HTTP_400_BAD_REQUEST
+            )
         except ProgrammingError as e:
-            if type(e.orig) == DuplicateTable:
-                raise DuplicateTableAPIException(
-                    e,
-                    message=f"Relation {validated_data['name']} already exists in schema {schema.id}",
-                    field="name",
-                    status_code=status.HTTP_400_BAD_REQUEST
-                )
-            else:
-                raise ProgrammingAPIException(e)
+            raise ProgrammingAPIException(e)
         return table
 
     def update(self, instance, validated_data):


Test patch:
diff --git a/mathesar/tests/imports/test_csv.py b/mathesar/tests/imports/test_csv.py
--- a/mathesar/tests/imports/test_csv.py
+++ b/mathesar/tests/imports/test_csv.py
@@ -1,7 +1,6 @@
 import pytest
 
 from django.core.files import File
-from sqlalchemy.exc import ProgrammingError
 from sqlalchemy import text
 
 from mathesar.models.base import DataFile, Schema
@@ -10,6 +9,7 @@
 from db.schemas.operations.create import create_schema
 from db.schemas.utils import get_schema_oid_from_name
 from db.constants import COLUMN_NAME_TEMPLATE
+from db.tables.operations.create import DuplicateTable
 
 TEST_SCHEMA = "import_csv_schema"
 
@@ -143,7 +143,6 @@ def test_col_headers_empty_csv(col_headers_empty_data_file, schema):
 
 def test_csv_upload_with_duplicate_table_name(data_file, schema):
     table_name = "NASA 2"
-    already_defined_str = 'relation "NASA 2" already exists'
 
     table = create_table_from_csv(data_file, table_name, schema)
     assert table is not None
@@ -151,9 +150,8 @@ def test_csv_upload_with_duplicate_table_name(data_file, schema):
     assert table.schema == schema
     assert table.sa_num_records() == 1393
 
-    with pytest.raises(ProgrammingError) as excinfo:
+    with pytest.raises(DuplicateTable):
         create_table_from_csv(data_file, table_name, schema)
-    assert already_defined_str in str(excinfo.value)
 
 
 def test_csv_upload_table_imported_to(data_file, schema):