prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Implement adding a new empty table
  ## Problem
  <!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->
  Users may want to create an empty table.
  
  ## Proposed solution
  <!-- A clear and concise description of your proposed solution or feature. -->
  The ["Basic Table Operations" design spec](https://wiki.mathesar.org/design/specs/table-operations) has a solution for this, which we need to implement on the frontend. We will be implementing a different design than the one in the spec, which will need to be improvised.
  
  This issue involves the following portions of the spec:
  - _User Experience_: 
    - User adds a new table
    - User edits a new table name
    - User adds an empty table
  - _Interactions_: 
    - Sequential Table Names
   
  Please note that we'll need to generate and display a default name of the table that the user can use. Following the logic in #449 is recommended.
  
  ## Additional context
  <!-- Add any other context or screenshots about the feature request here.-->
  - Backend work: #184 
  - Design issue: #185
  - #449 is related.
  
  Logs of conversation on Matrix from @pavish:
  
  > For adding an empty table, instead of showing Empty table option within the tab, I think it might be better to show it on the top.
  > 
  > The Add new table button can be changed to a dropdown with two options. 1. Empty table, 2. Import data.
  > 
  > On clicking empty table, it would just send a create table request and create the table. Then the user can rename it if they want.
  > 
  > This will have clean separation of concerns. Because Import data, involves importing from csv, remote file import and then copy/paste from spreadsheet..
  > 
  > Empty table, would just create a new empty table.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `mathesar/api/viewsets/tables.py`
  Content:
  ```
  1 from django_filters import rest_framework as filters
  2 from psycopg2.errors import CheckViolation, DuplicateTable, InvalidTextRepresentation
  3 from rest_framework import status, viewsets
  4 from rest_framework.decorators import action
  5 from rest_framework.exceptions import ValidationError, APIException
  6 from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
  7 from rest_framework.response import Response
  8 from sqlalchemy.exc import ProgrammingError, DataError, IntegrityError
  9 
  10 from db.types.alteration import UnsupportedTypeException
  11 from mathesar.api.filters import TableFilter
  12 from mathesar.api.pagination import DefaultLimitOffsetPagination
  13 from mathesar.api.serializers.tables import TableSerializer, TablePreviewSerializer
  14 from mathesar.models import Table
  15 from mathesar.utils.tables import (
  16     get_table_column_types, create_table_from_datafile, create_empty_table,
  17     gen_table_name
  18 )
  19 
  20 
  21 class TableViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):
  22     serializer_class = TableSerializer
  23     pagination_class = DefaultLimitOffsetPagination
  24     filter_backends = (filters.DjangoFilterBackend,)
  25     filterset_class = TableFilter
  26 
  27     def get_queryset(self):
  28         return Table.objects.all().order_by('-created_at')
  29 
  30     def create(self, request):
  31         serializer = TableSerializer(data=request.data, context={'request': request})
  32         serializer.is_valid(raise_exception=True)
  33 
  34         if not serializer.validated_data['name']:
  35             name = gen_table_name(
  36                 serializer.validated_data['schema'],
  37                 serializer.validated_data['data_files'],
  38             )
  39         else:
  40             name = serializer.validated_data['name']
  41 
  42         try:
  43             if serializer.validated_data['data_files']:
  44                 table = create_table_from_datafile(
  45                     serializer.validated_data['data_files'],
  46                     name,
  47                     serializer.validated_data['schema'],
  48                 )
  49             else:
  50                 table = create_empty_table(
  51                     name,
  52                     serializer.validated_data['schema']
  53                 )
  54         except ProgrammingError as e:
  55             if type(e.orig) == DuplicateTable:
  56                 raise ValidationError(
  57                     f"Relation {request.data['name']} already exists in schema {request.data['schema']}"
  58                 )
  59             else:
  60                 raise APIException(e)
  61 
  62         serializer = TableSerializer(table, context={'request': request})
  63         return Response(serializer.data, status=status.HTTP_201_CREATED)
  64 
  65     def partial_update(self, request, pk=None):
  66         serializer = TableSerializer(
  67             data=request.data, context={'request': request}, partial=True
  68         )
  69         serializer.is_valid(raise_exception=True)
  70         table = self.get_object()
  71 
  72         # Save the fields that are stored in the model.
  73         present_model_fields = []
  74         for model_field in table.MODEL_FIELDS:
  75             if model_field in serializer.validated_data:
  76                 setattr(table, model_field, serializer.validated_data[model_field])
  77                 present_model_fields.append(model_field)
  78         table.save(update_fields=present_model_fields)
  79         for key in present_model_fields:
  80             del serializer.validated_data[key]
  81 
  82         # Save the fields that are stored in the underlying DB.
  83         try:
  84             table.update_sa_table(serializer.validated_data)
  85         except ValueError as e:
  86             raise ValidationError(e)
  87 
  88         # Reload the table to avoid cached properties
  89         table = self.get_object()
  90         serializer = TableSerializer(table, context={'request': request})
  91         return Response(serializer.data)
  92 
  93     def destroy(self, request, pk=None):
  94         table = self.get_object()
  95         table.delete_sa_table()
  96         table.delete()
  97         return Response(status=status.HTTP_204_NO_CONTENT)
  98 
  99     @action(methods=['get'], detail=True)
  100     def type_suggestions(self, request, pk=None):
  101         table = self.get_object()
  102         col_types = get_table_column_types(table)
  103         return Response(col_types)
  104 
  105     @action(methods=['post'], detail=True)
  106     def previews(self, request, pk=None):
  107         table = self.get_object()
  108         serializer = TablePreviewSerializer(data=request.data)
  109         serializer.is_valid(raise_exception=True)
  110         columns = serializer.data["columns"]
  111 
  112         column_names = [col["name"] for col in columns]
  113         if not len(column_names) == len(set(column_names)):
  114             raise ValidationError("Column names must be distinct")
  115         if not len(columns) == len(table.sa_columns):
  116             raise ValidationError("Incorrect number of columns in request.")
  117 
  118         table_data = TableSerializer(table, context={"request": request}).data
  119         try:
  120             preview_records = table.get_preview(columns)
  121         except (DataError, IntegrityError) as e:
  122             if type(e.orig) == InvalidTextRepresentation or type(e.orig) == CheckViolation:
  123                 raise ValidationError("Invalid type cast requested.")
  124             else:
  125                 raise APIException
  126         except UnsupportedTypeException as e:
  127             raise ValidationError(e)
  128         except Exception as e:
  129             raise APIException(e)
  130 
  131         table_data.update(
  132             {
  133                 # There's no way to reflect actual column data without
  134                 # creating a view, so we just use the submission, assuming
  135                 # no errors means we changed to the desired names and types
  136                 "columns": columns,
  137                 "records": preview_records
  138             }
  139         )
  140 
  141         return Response(table_data)
  142
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/mathesar/api/viewsets/tables.py b/mathesar/api/viewsets/tables.py
  --- a/mathesar/api/viewsets/tables.py
  +++ b/mathesar/api/viewsets/tables.py
  @@ -31,26 +31,15 @@
           serializer = TableSerializer(data=request.data, context={'request': request})
           serializer.is_valid(raise_exception=True)
   
  -        if not serializer.validated_data['name']:
  -            name = gen_table_name(
  -                serializer.validated_data['schema'],
  -                serializer.validated_data['data_files'],
  -            )
  -        else:
  -            name = serializer.validated_data['name']
  +        schema = serializer.validated_data['schema']
  +        data_files = serializer.validated_data.get('data_files')
  +        name = serializer.validated_data.get('name') or gen_table_name(schema, data_files)
   
           try:
  -            if serializer.validated_data['data_files']:
  -                table = create_table_from_datafile(
  -                    serializer.validated_data['data_files'],
  -                    name,
  -                    serializer.validated_data['schema'],
  -                )
  +            if data_files:
  +                table = create_table_from_datafile(data_files, name, schema)
               else:
  -                table = create_empty_table(
  -                    name,
  -                    serializer.validated_data['schema']
  -                )
  +                table = create_empty_table(name, schema)
           except ProgrammingError as e:
               if type(e.orig) == DuplicateTable:
                   raise ValidationError(
  
verification_info: {"golden_diff": "diff --git a/mathesar/api/viewsets/tables.py b/mathesar/api/viewsets/tables.py\n--- a/mathesar/api/viewsets/tables.py\n+++ b/mathesar/api/viewsets/tables.py\n@@ -31,26 +31,15 @@\n         serializer = TableSerializer(data=request.data, context={'request': request})\n         serializer.is_valid(raise_exception=True)\n \n-        if not serializer.validated_data['name']:\n-            name = gen_table_name(\n-                serializer.validated_data['schema'],\n-                serializer.validated_data['data_files'],\n-            )\n-        else:\n-            name = serializer.validated_data['name']\n+        schema = serializer.validated_data['schema']\n+        data_files = serializer.validated_data.get('data_files')\n+        name = serializer.validated_data.get('name') or gen_table_name(schema, data_files)\n \n         try:\n-            if serializer.validated_data['data_files']:\n-                table = create_table_from_datafile(\n-                    serializer.validated_data['data_files'],\n-                    name,\n-                    serializer.validated_data['schema'],\n-                )\n+            if data_files:\n+                table = create_table_from_datafile(data_files, name, schema)\n             else:\n-                table = create_empty_table(\n-                    name,\n-                    serializer.validated_data['schema']\n-                )\n+                table = create_empty_table(name, schema)\n         except ProgrammingError as e:\n             if type(e.orig) == DuplicateTable:\n                 raise ValidationError(\n", "issue": "Implement adding a new empty table\n## Problem\r\n<!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->\r\nUsers may want to create an empty table.\r\n\r\n## Proposed solution\r\n<!-- A clear and concise description of your proposed solution or feature. -->\r\nThe [\"Basic Table Operations\" design spec](https://wiki.mathesar.org/design/specs/table-operations) has a solution for this, which we need to implement on the frontend. We will be implementing a different design than the one in the spec, which will need to be improvised.\r\n\r\nThis issue involves the following portions of the spec:\r\n- _User Experience_: \r\n  - User adds a new table\r\n  - User edits a new table name\r\n  - User adds an empty table\r\n- _Interactions_: \r\n  - Sequential Table Names\r\n \r\nPlease note that we'll need to generate and display a default name of the table that the user can use. Following the logic in #449 is recommended.\r\n\r\n## Additional context\r\n<!-- Add any other context or screenshots about the feature request here.-->\r\n- Backend work: #184 \r\n- Design issue: #185\r\n- #449 is related.\r\n\r\nLogs of conversation on Matrix from @pavish:\r\n\r\n> For adding an empty table, instead of showing Empty table option within the tab, I think it might be better to show it on the top.\r\n> \r\n> The Add new table button can be changed to a dropdown with two options. 1. Empty table, 2. Import data.\r\n> \r\n> On clicking empty table, it would just send a create table request and create the table. Then the user can rename it if they want.\r\n> \r\n> This will have clean separation of concerns. Because Import data, involves importing from csv, remote file import and then copy/paste from spreadsheet..\r\n> \r\n> Empty table, would just create a new empty table.\n", "before_files": [{"content": "from django_filters import rest_framework as filters\nfrom psycopg2.errors import CheckViolation, DuplicateTable, InvalidTextRepresentation\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import ValidationError, APIException\nfrom rest_framework.mixins import ListModelMixin, RetrieveModelMixin\nfrom rest_framework.response import Response\nfrom sqlalchemy.exc import ProgrammingError, DataError, IntegrityError\n\nfrom db.types.alteration import UnsupportedTypeException\nfrom mathesar.api.filters import TableFilter\nfrom mathesar.api.pagination import DefaultLimitOffsetPagination\nfrom mathesar.api.serializers.tables import TableSerializer, TablePreviewSerializer\nfrom mathesar.models import Table\nfrom mathesar.utils.tables import (\n    get_table_column_types, create_table_from_datafile, create_empty_table,\n    gen_table_name\n)\n\n\nclass TableViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):\n    serializer_class = TableSerializer\n    pagination_class = DefaultLimitOffsetPagination\n    filter_backends = (filters.DjangoFilterBackend,)\n    filterset_class = TableFilter\n\n    def get_queryset(self):\n        return Table.objects.all().order_by('-created_at')\n\n    def create(self, request):\n        serializer = TableSerializer(data=request.data, context={'request': request})\n        serializer.is_valid(raise_exception=True)\n\n        if not serializer.validated_data['name']:\n            name = gen_table_name(\n                serializer.validated_data['schema'],\n                serializer.validated_data['data_files'],\n            )\n        else:\n            name = serializer.validated_data['name']\n\n        try:\n            if serializer.validated_data['data_files']:\n                table = create_table_from_datafile(\n                    serializer.validated_data['data_files'],\n                    name,\n                    serializer.validated_data['schema'],\n                )\n            else:\n                table = create_empty_table(\n                    name,\n                    serializer.validated_data['schema']\n                )\n        except ProgrammingError as e:\n            if type(e.orig) == DuplicateTable:\n                raise ValidationError(\n                    f\"Relation {request.data['name']} already exists in schema {request.data['schema']}\"\n                )\n            else:\n                raise APIException(e)\n\n        serializer = TableSerializer(table, context={'request': request})\n        return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n    def partial_update(self, request, pk=None):\n        serializer = TableSerializer(\n            data=request.data, context={'request': request}, partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n        table = self.get_object()\n\n        # Save the fields that are stored in the model.\n        present_model_fields = []\n        for model_field in table.MODEL_FIELDS:\n            if model_field in serializer.validated_data:\n                setattr(table, model_field, serializer.validated_data[model_field])\n                present_model_fields.append(model_field)\n        table.save(update_fields=present_model_fields)\n        for key in present_model_fields:\n            del serializer.validated_data[key]\n\n        # Save the fields that are stored in the underlying DB.\n        try:\n            table.update_sa_table(serializer.validated_data)\n        except ValueError as e:\n            raise ValidationError(e)\n\n        # Reload the table to avoid cached properties\n        table = self.get_object()\n        serializer = TableSerializer(table, context={'request': request})\n        return Response(serializer.data)\n\n    def destroy(self, request, pk=None):\n        table = self.get_object()\n        table.delete_sa_table()\n        table.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    @action(methods=['get'], detail=True)\n    def type_suggestions(self, request, pk=None):\n        table = self.get_object()\n        col_types = get_table_column_types(table)\n        return Response(col_types)\n\n    @action(methods=['post'], detail=True)\n    def previews(self, request, pk=None):\n        table = self.get_object()\n        serializer = TablePreviewSerializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        columns = serializer.data[\"columns\"]\n\n        column_names = [col[\"name\"] for col in columns]\n        if not len(column_names) == len(set(column_names)):\n            raise ValidationError(\"Column names must be distinct\")\n        if not len(columns) == len(table.sa_columns):\n            raise ValidationError(\"Incorrect number of columns in request.\")\n\n        table_data = TableSerializer(table, context={\"request\": request}).data\n        try:\n            preview_records = table.get_preview(columns)\n        except (DataError, IntegrityError) as e:\n            if type(e.orig) == InvalidTextRepresentation or type(e.orig) == CheckViolation:\n                raise ValidationError(\"Invalid type cast requested.\")\n            else:\n                raise APIException\n        except UnsupportedTypeException as e:\n            raise ValidationError(e)\n        except Exception as e:\n            raise APIException(e)\n\n        table_data.update(\n            {\n                # There's no way to reflect actual column data without\n                # creating a view, so we just use the submission, assuming\n                # no errors means we changed to the desired names and types\n                \"columns\": columns,\n                \"records\": preview_records\n            }\n        )\n\n        return Response(table_data)\n", "path": "mathesar/api/viewsets/tables.py"}], "after_files": [{"content": "from django_filters import rest_framework as filters\nfrom psycopg2.errors import CheckViolation, DuplicateTable, InvalidTextRepresentation\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.exceptions import ValidationError, APIException\nfrom rest_framework.mixins import ListModelMixin, RetrieveModelMixin\nfrom rest_framework.response import Response\nfrom sqlalchemy.exc import ProgrammingError, DataError, IntegrityError\n\nfrom db.types.alteration import UnsupportedTypeException\nfrom mathesar.api.filters import TableFilter\nfrom mathesar.api.pagination import DefaultLimitOffsetPagination\nfrom mathesar.api.serializers.tables import TableSerializer, TablePreviewSerializer\nfrom mathesar.models import Table\nfrom mathesar.utils.tables import (\n    get_table_column_types, create_table_from_datafile, create_empty_table,\n    gen_table_name\n)\n\n\nclass TableViewSet(viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):\n    serializer_class = TableSerializer\n    pagination_class = DefaultLimitOffsetPagination\n    filter_backends = (filters.DjangoFilterBackend,)\n    filterset_class = TableFilter\n\n    def get_queryset(self):\n        return Table.objects.all().order_by('-created_at')\n\n    def create(self, request):\n        serializer = TableSerializer(data=request.data, context={'request': request})\n        serializer.is_valid(raise_exception=True)\n\n        schema = serializer.validated_data['schema']\n        data_files = serializer.validated_data.get('data_files')\n        name = serializer.validated_data.get('name') or gen_table_name(schema, data_files)\n\n        try:\n            if data_files:\n                table = create_table_from_datafile(data_files, name, schema)\n            else:\n                table = create_empty_table(name, schema)\n        except ProgrammingError as e:\n            if type(e.orig) == DuplicateTable:\n                raise ValidationError(\n                    f\"Relation {request.data['name']} already exists in schema {request.data['schema']}\"\n                )\n            else:\n                raise APIException(e)\n\n        serializer = TableSerializer(table, context={'request': request})\n        return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n    def partial_update(self, request, pk=None):\n        serializer = TableSerializer(\n            data=request.data, context={'request': request}, partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n        table = self.get_object()\n\n        # Save the fields that are stored in the model.\n        present_model_fields = []\n        for model_field in table.MODEL_FIELDS:\n            if model_field in serializer.validated_data:\n                setattr(table, model_field, serializer.validated_data[model_field])\n                present_model_fields.append(model_field)\n        table.save(update_fields=present_model_fields)\n        for key in present_model_fields:\n            del serializer.validated_data[key]\n\n        # Save the fields that are stored in the underlying DB.\n        try:\n            table.update_sa_table(serializer.validated_data)\n        except ValueError as e:\n            raise ValidationError(e)\n\n        # Reload the table to avoid cached properties\n        table = self.get_object()\n        serializer = TableSerializer(table, context={'request': request})\n        return Response(serializer.data)\n\n    def destroy(self, request, pk=None):\n        table = self.get_object()\n        table.delete_sa_table()\n        table.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    @action(methods=['get'], detail=True)\n    def type_suggestions(self, request, pk=None):\n        table = self.get_object()\n        col_types = get_table_column_types(table)\n        return Response(col_types)\n\n    @action(methods=['post'], detail=True)\n    def previews(self, request, pk=None):\n        table = self.get_object()\n        serializer = TablePreviewSerializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        columns = serializer.data[\"columns\"]\n\n        column_names = [col[\"name\"] for col in columns]\n        if not len(column_names) == len(set(column_names)):\n            raise ValidationError(\"Column names must be distinct\")\n        if not len(columns) == len(table.sa_columns):\n            raise ValidationError(\"Incorrect number of columns in request.\")\n\n        table_data = TableSerializer(table, context={\"request\": request}).data\n        try:\n            preview_records = table.get_preview(columns)\n        except (DataError, IntegrityError) as e:\n            if type(e.orig) == InvalidTextRepresentation or type(e.orig) == CheckViolation:\n                raise ValidationError(\"Invalid type cast requested.\")\n            else:\n                raise APIException\n        except UnsupportedTypeException as e:\n            raise ValidationError(e)\n        except Exception as e:\n            raise APIException(e)\n\n        table_data.update(\n            {\n                # There's no way to reflect actual column data without\n                # creating a view, so we just use the submission, assuming\n                # no errors means we changed to the desired names and types\n                \"columns\": columns,\n                \"records\": preview_records\n            }\n        )\n\n        return Response(table_data)\n", "path": "mathesar/api/viewsets/tables.py"}]}