Repository: mathesar-foundation/mathesar

Issue:
Implement `/existing_import` API endpoint.
## Problem
We want to implement an `existing_import` API endpoint that will be exposed to the frontend which will allow us to move/import records from the temp table to an existing table. 
  
## Proposed Solution
The URL for the endpoint should look something like: `api/db/vX/tables/{temp_table_id}/existing_import/` and the request payload should look like this:
### Request
```json
{
	"table_to_import_to": "<existing_table_id>",
	"data-files":[10],
	"mappings":{
	    "id": "id",
            "Name": "Center",
            "Status": "Status",
            "Case_id:": "Case_Number"
        }
}

```


Discussion:

Resolving patch:
diff --git a/mathesar/api/db/viewsets/tables.py b/mathesar/api/db/viewsets/tables.py
--- a/mathesar/api/db/viewsets/tables.py
+++ b/mathesar/api/db/viewsets/tables.py
@@ -8,6 +8,7 @@
 
 from db.tables.operations.select import get_oid_from_table
 from db.types.exceptions import UnsupportedTypeException
+from mathesar.api.utils import get_table_or_404
 from mathesar.api.dj_filters import TableFilter
 from mathesar.api.exceptions.database_exceptions import (
     base_exceptions as database_base_api_exceptions,
@@ -15,7 +16,7 @@
 )
 from mathesar.api.pagination import DefaultLimitOffsetPagination
 from mathesar.api.serializers.tables import (
-    SplitTableRequestSerializer, SplitTableResponseSerializer, TablePreviewSerializer, TableSerializer,
+    SplitTableRequestSerializer, SplitTableResponseSerializer, TablePreviewSerializer, TableSerializer, TableImportSerializer
 )
 from mathesar.models import Table
 from mathesar.reflection import reflect_db_objects, reflect_tables_from_schema
@@ -146,3 +147,27 @@ def previews(self, request, pk=None):
         )
 
         return Response(table_data)
+
+    @action(methods=['post'], detail=True)
+    def existing_import(self, request, pk=None):
+        temp_table = self.get_object()
+        serializer = TableImportSerializer(data=request.data, context={"request": request})
+        serializer.is_valid(raise_exception=True)
+        existing_table_id = serializer.validated_data['table_to_import_to']
+        mappings = serializer.validated_data['mappings']
+        existing_table = get_table_or_404(existing_table_id)
+
+        try:
+            temp_table.insert_records_to_existing_table(
+                existing_table, temp_table, mappings
+            )
+        except Exception as e:
+            # ToDo raise specific exceptions.
+            raise e
+        # Reload the table to avoid cached properties
+        existing_table = get_table_or_404(existing_table_id)
+        serializer = TableSerializer(
+            existing_table, context={'request': request}
+        )
+        table_data = serializer.data
+        return Response(table_data)
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
@@ -170,3 +170,13 @@ def validate(self, attrs):
 class SplitTableResponseSerializer(MathesarErrorMessageMixin, serializers.Serializer):
     extracted_table = serializers.PrimaryKeyRelatedField(queryset=Table.current_objects.all())
     remainder_table = serializers.PrimaryKeyRelatedField(queryset=Table.current_objects.all())
+
+
+class MappingSerializer(MathesarErrorMessageMixin, serializers.Serializer):
+    # TBD
+    pass
+
+
+class TableImportSerializer(MathesarErrorMessageMixin, serializers.Serializer):
+    table_to_import_to = serializers.PrimaryKeyRelatedField(queryset=Table.current_objects.all(), required=True)
+    mappings = MappingSerializer(required=True)
diff --git a/mathesar/models.py b/mathesar/models.py
--- a/mathesar/models.py
+++ b/mathesar/models.py
@@ -391,6 +391,10 @@ def update_column_reference(self, columns_name, column_name_id_map):
             column_objs.append(column)
         Column.current_objects.bulk_update(column_objs, fields=['table_id', 'attnum'])
 
+    def insert_records_to_existing_table(existing_table, temp_table, mappings):
+        # TBD
+        pass
+
 
 class Column(ReflectionManagerMixin, BaseModel):
     table = models.ForeignKey('Table', on_delete=models.CASCADE, related_name='columns')


Test patch: