We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix API error response when filtering on an invalid date
## Steps to reproduce

1. Send a request like

    ```http
    GET http://localhost:8000/api/db/v0/tables/1/records/?limit=500&offset=0&filter={"equal":[{"column_id":[6]},{"literal":["2"]}]}
    ```

    Here, column with id `6` is a date column, and the value `"2"` is a partially-entered date that should be considered invalid.

1. Expect an error response conforming to our [error standards](https://wiki.mathesar.org/en/engineering/standards/api#errors)

1. Instead, observe an HTTP 500 Django error.


--- 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/db/viewsets/records.py`
Content:
```
1 from psycopg2.errors import ForeignKeyViolation
2 from rest_access_policy import AccessViewSetMixin
3 from rest_framework import status, viewsets
4 from rest_framework.exceptions import NotFound
5 from rest_framework.renderers import BrowsableAPIRenderer
6 from rest_framework.response import Response
7 from sqlalchemy.exc import IntegrityError
8 
9 from mathesar.api.db.permissions.records import RecordAccessPolicy
10 from mathesar.api.exceptions.error_codes import ErrorCodes
11 import mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions
12 import mathesar.api.exceptions.generic_exceptions.base_exceptions as generic_api_exceptions
13 from db.functions.exceptions import (
14     BadDBFunctionFormat, ReferencedColumnsDontExist, UnknownDBFunctionID,
15 )
16 from db.records.exceptions import (
17     BadGroupFormat, GroupFieldNotFound, InvalidGroupType, UndefinedFunction,
18     BadSortFormat, SortFieldNotFound
19 )
20 from mathesar.api.pagination import TableLimitOffsetPagination
21 from mathesar.api.serializers.records import RecordListParameterSerializer, RecordSerializer
22 from mathesar.api.utils import get_table_or_404
23 from mathesar.functions.operations.convert import rewrite_db_function_spec_column_ids_to_names
24 from mathesar.models.base import Table
25 from mathesar.utils.json import MathesarJSONRenderer
26 
27 
28 class RecordViewSet(AccessViewSetMixin, viewsets.ViewSet):
29     access_policy = RecordAccessPolicy
30 
31     # There is no 'update' method.
32     # We're not supporting PUT requests because there aren't a lot of use cases
33     # where the entire record needs to be replaced, PATCH suffices for updates.
34     def get_queryset(self):
35         return Table.objects.all().order_by('-created_at')
36 
37     renderer_classes = [MathesarJSONRenderer, BrowsableAPIRenderer]
38 
39     # For filter parameter formatting, see:
40     # db/functions/operations/deserialize.py::get_db_function_from_ma_function_spec function doc>
41     # For sorting parameter formatting, see:
42     # https://github.com/centerofci/sqlalchemy-filters#sort-format
43     def list(self, request, table_pk=None):
44         paginator = TableLimitOffsetPagination()
45 
46         serializer = RecordListParameterSerializer(data=request.GET)
47         serializer.is_valid(raise_exception=True)
48         table = get_table_or_404(table_pk)
49 
50         filter_unprocessed = serializer.validated_data['filter']
51         order_by = serializer.validated_data['order_by']
52         grouping = serializer.validated_data['grouping']
53         search_fuzzy = serializer.validated_data['search_fuzzy']
54         filter_processed = None
55         column_names_to_ids = table.get_column_name_id_bidirectional_map()
56         column_ids_to_names = column_names_to_ids.inverse
57         if filter_unprocessed:
58             filter_processed = rewrite_db_function_spec_column_ids_to_names(
59                 column_ids_to_names=column_ids_to_names,
60                 spec=filter_unprocessed,
61             )
62         # Replace column id value used in the `field` property with column name
63         name_converted_group_by = None
64         if grouping:
65             group_by_columns_names = [column_ids_to_names[column_id] for column_id in grouping['columns']]
66             name_converted_group_by = {**grouping, 'columns': group_by_columns_names}
67         name_converted_order_by = [{**column, 'field': column_ids_to_names[column['field']]} for column in order_by]
68         name_converted_search = [{**column, 'column': column_ids_to_names[column['field']]} for column in search_fuzzy]
69 
70         try:
71 
72             records = paginator.paginate_queryset(
73                 self.get_queryset(), request, table, column_names_to_ids,
74                 filters=filter_processed,
75                 order_by=name_converted_order_by,
76                 grouping=name_converted_group_by,
77                 search=name_converted_search,
78                 duplicate_only=serializer.validated_data['duplicate_only']
79             )
80         except (BadDBFunctionFormat, UnknownDBFunctionID, ReferencedColumnsDontExist) as e:
81             raise database_api_exceptions.BadFilterAPIException(
82                 e,
83                 field='filters',
84                 status_code=status.HTTP_400_BAD_REQUEST
85             )
86         except (BadSortFormat, SortFieldNotFound) as e:
87             raise database_api_exceptions.BadSortAPIException(
88                 e,
89                 field='order_by',
90                 status_code=status.HTTP_400_BAD_REQUEST
91             )
92         except (BadGroupFormat, GroupFieldNotFound, InvalidGroupType) as e:
93             raise database_api_exceptions.BadGroupAPIException(
94                 e,
95                 field='grouping',
96                 status_code=status.HTTP_400_BAD_REQUEST
97             )
98         except UndefinedFunction as e:
99             raise database_api_exceptions.UndefinedFunctionAPIException(
100                 e,
101                 details=e.args[0],
102                 status_code=status.HTTP_400_BAD_REQUEST
103             )
104         serializer = RecordSerializer(
105             records,
106             many=True,
107             context=self.get_serializer_context(table)
108         )
109         return paginator.get_paginated_response(serializer.data)
110 
111     def retrieve(self, request, pk=None, table_pk=None):
112         table = get_table_or_404(table_pk)
113         # TODO refactor to use serializer for more DRY response logic
114         paginator = TableLimitOffsetPagination()
115         record_filters = {
116             "equal": [
117                 {"column_name": [table.primary_key_column_name]},
118                 {"literal": [pk]}
119             ]
120         }
121         column_names_to_ids = table.get_column_name_id_bidirectional_map()
122         records = paginator.paginate_queryset(
123             table,
124             request,
125             table,
126             column_names_to_ids,
127             filters=record_filters
128         )
129         if not records:
130             raise NotFound
131         serializer = RecordSerializer(
132             records,
133             many=True,
134             context=self.get_serializer_context(table)
135         )
136         return paginator.get_paginated_response(serializer.data)
137 
138     def create(self, request, table_pk=None):
139         table = get_table_or_404(table_pk)
140         serializer = RecordSerializer(data=request.data, context=self.get_serializer_context(table))
141         serializer.is_valid(raise_exception=True)
142         serializer.save()
143         # TODO refactor to use serializer for more DRY response logic
144         column_name_id_map = table.get_column_name_id_bidirectional_map()
145         table_pk_column_id = column_name_id_map[table.primary_key_column_name]
146         pk_value = serializer.data[table_pk_column_id]
147         paginator = TableLimitOffsetPagination()
148         record_filters = {
149             "equal": [
150                 {"column_name": [table.primary_key_column_name]},
151                 {"literal": [pk_value]}
152             ]
153         }
154         column_names_to_ids = table.get_column_name_id_bidirectional_map()
155         records = paginator.paginate_queryset(
156             table,
157             request,
158             table,
159             column_names_to_ids,
160             filters=record_filters
161         )
162         serializer = RecordSerializer(
163             records,
164             many=True,
165             context=self.get_serializer_context(table)
166         )
167         response = paginator.get_paginated_response(serializer.data)
168         response.status_code = status.HTTP_201_CREATED
169         return response
170 
171     def partial_update(self, request, pk=None, table_pk=None):
172         table = get_table_or_404(table_pk)
173         serializer = RecordSerializer(
174             {'id': pk},
175             data=request.data,
176             context=self.get_serializer_context(table),
177             partial=True
178         )
179         serializer.is_valid(raise_exception=True)
180         serializer.save()
181         # TODO refactor to use serializer for more DRY response logic
182         paginator = TableLimitOffsetPagination()
183         record_filters = {
184             "equal": [
185                 {"column_name": [table.primary_key_column_name]},
186                 {"literal": [pk]}
187             ]
188         }
189         column_names_to_ids = table.get_column_name_id_bidirectional_map()
190         records = paginator.paginate_queryset(
191             table,
192             request,
193             table,
194             column_names_to_ids,
195             filters=record_filters
196         )
197         serializer = RecordSerializer(
198             records,
199             many=True,
200             context=self.get_serializer_context(table)
201         )
202         return paginator.get_paginated_response(serializer.data)
203 
204     def destroy(self, request, pk=None, table_pk=None):
205         table = get_table_or_404(table_pk)
206         if table.get_record(pk) is None:
207             raise generic_api_exceptions.NotFoundAPIException(
208                 NotFound,
209                 error_code=ErrorCodes.RecordNotFound.value,
210                 message="Record doesn't exist"
211             )
212         try:
213             table.delete_record(pk)
214         except IntegrityError as e:
215             if isinstance(e.orig, ForeignKeyViolation):
216                 raise database_api_exceptions.ForeignKeyViolationAPIException(
217                     e,
218                     status_code=status.HTTP_400_BAD_REQUEST,
219                     referent_table=table,
220                 )
221 
222         return Response(status=status.HTTP_204_NO_CONTENT)
223 
224     def get_serializer_context(self, table):
225         columns_map = table.get_column_name_id_bidirectional_map()
226         context = {'columns_map': columns_map, 'table': table}
227         return context
228
```
--- 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()

```

diff --git a/mathesar/api/db/viewsets/records.py b/mathesar/api/db/viewsets/records.py
--- a/mathesar/api/db/viewsets/records.py
+++ b/mathesar/api/db/viewsets/records.py
@@ -1,10 +1,10 @@
-from psycopg2.errors import ForeignKeyViolation
+from psycopg2.errors import ForeignKeyViolation, InvalidDatetimeFormat
 from rest_access_policy import AccessViewSetMixin
 from rest_framework import status, viewsets
 from rest_framework.exceptions import NotFound
 from rest_framework.renderers import BrowsableAPIRenderer
 from rest_framework.response import Response
-from sqlalchemy.exc import IntegrityError
+from sqlalchemy.exc import IntegrityError, DataError
 
 from mathesar.api.db.permissions.records import RecordAccessPolicy
 from mathesar.api.exceptions.error_codes import ErrorCodes
@@ -101,6 +101,13 @@
                 details=e.args[0],
                 status_code=status.HTTP_400_BAD_REQUEST
             )
+        except DataError as e:
+            if isinstance(e.orig, InvalidDatetimeFormat):
+                raise database_api_exceptions.InvalidDateFormatAPIException(
+                    e,
+                    status_code=status.HTTP_400_BAD_REQUEST,
+                )
+
         serializer = RecordSerializer(
             records,
             many=True,

{"golden_diff": "diff --git a/mathesar/api/db/viewsets/records.py b/mathesar/api/db/viewsets/records.py\n--- a/mathesar/api/db/viewsets/records.py\n+++ b/mathesar/api/db/viewsets/records.py\n@@ -1,10 +1,10 @@\n-from psycopg2.errors import ForeignKeyViolation\n+from psycopg2.errors import ForeignKeyViolation, InvalidDatetimeFormat\n from rest_access_policy import AccessViewSetMixin\n from rest_framework import status, viewsets\n from rest_framework.exceptions import NotFound\n from rest_framework.renderers import BrowsableAPIRenderer\n from rest_framework.response import Response\n-from sqlalchemy.exc import IntegrityError\n+from sqlalchemy.exc import IntegrityError, DataError\n \n from mathesar.api.db.permissions.records import RecordAccessPolicy\n from mathesar.api.exceptions.error_codes import ErrorCodes\n@@ -101,6 +101,13 @@\n                 details=e.args[0],\n                 status_code=status.HTTP_400_BAD_REQUEST\n             )\n+        except DataError as e:\n+            if isinstance(e.orig, InvalidDatetimeFormat):\n+                raise database_api_exceptions.InvalidDateFormatAPIException(\n+                    e,\n+                    status_code=status.HTTP_400_BAD_REQUEST,\n+                )\n+\n         serializer = RecordSerializer(\n             records,\n             many=True,\n", "issue": "Fix API error response when filtering on an invalid date\n## Steps to reproduce\n\n1. Send a request like\n\n    ```http\n    GET http://localhost:8000/api/db/v0/tables/1/records/?limit=500&offset=0&filter={\"equal\":[{\"column_id\":[6]},{\"literal\":[\"2\"]}]}\n    ```\n\n    Here, column with id `6` is a date column, and the value `\"2\"` is a partially-entered date that should be considered invalid.\n\n1. Expect an error response conforming to our [error standards](https://wiki.mathesar.org/en/engineering/standards/api#errors)\n\n1. Instead, observe an HTTP 500 Django error.\n\n", "before_files": [{"content": "from psycopg2.errors import ForeignKeyViolation\nfrom rest_access_policy import AccessViewSetMixin\nfrom rest_framework import status, viewsets\nfrom rest_framework.exceptions import NotFound\nfrom rest_framework.renderers import BrowsableAPIRenderer\nfrom rest_framework.response import Response\nfrom sqlalchemy.exc import IntegrityError\n\nfrom mathesar.api.db.permissions.records import RecordAccessPolicy\nfrom mathesar.api.exceptions.error_codes import ErrorCodes\nimport mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions\nimport mathesar.api.exceptions.generic_exceptions.base_exceptions as generic_api_exceptions\nfrom db.functions.exceptions import (\n    BadDBFunctionFormat, ReferencedColumnsDontExist, UnknownDBFunctionID,\n)\nfrom db.records.exceptions import (\n    BadGroupFormat, GroupFieldNotFound, InvalidGroupType, UndefinedFunction,\n    BadSortFormat, SortFieldNotFound\n)\nfrom mathesar.api.pagination import TableLimitOffsetPagination\nfrom mathesar.api.serializers.records import RecordListParameterSerializer, RecordSerializer\nfrom mathesar.api.utils import get_table_or_404\nfrom mathesar.functions.operations.convert import rewrite_db_function_spec_column_ids_to_names\nfrom mathesar.models.base import Table\nfrom mathesar.utils.json import MathesarJSONRenderer\n\n\nclass RecordViewSet(AccessViewSetMixin, viewsets.ViewSet):\n    access_policy = RecordAccessPolicy\n\n    # There is no 'update' method.\n    # We're not supporting PUT requests because there aren't a lot of use cases\n    # where the entire record needs to be replaced, PATCH suffices for updates.\n    def get_queryset(self):\n        return Table.objects.all().order_by('-created_at')\n\n    renderer_classes = [MathesarJSONRenderer, BrowsableAPIRenderer]\n\n    # For filter parameter formatting, see:\n    # db/functions/operations/deserialize.py::get_db_function_from_ma_function_spec function doc>\n    # For sorting parameter formatting, see:\n    # https://github.com/centerofci/sqlalchemy-filters#sort-format\n    def list(self, request, table_pk=None):\n        paginator = TableLimitOffsetPagination()\n\n        serializer = RecordListParameterSerializer(data=request.GET)\n        serializer.is_valid(raise_exception=True)\n        table = get_table_or_404(table_pk)\n\n        filter_unprocessed = serializer.validated_data['filter']\n        order_by = serializer.validated_data['order_by']\n        grouping = serializer.validated_data['grouping']\n        search_fuzzy = serializer.validated_data['search_fuzzy']\n        filter_processed = None\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        column_ids_to_names = column_names_to_ids.inverse\n        if filter_unprocessed:\n            filter_processed = rewrite_db_function_spec_column_ids_to_names(\n                column_ids_to_names=column_ids_to_names,\n                spec=filter_unprocessed,\n            )\n        # Replace column id value used in the `field` property with column name\n        name_converted_group_by = None\n        if grouping:\n            group_by_columns_names = [column_ids_to_names[column_id] for column_id in grouping['columns']]\n            name_converted_group_by = {**grouping, 'columns': group_by_columns_names}\n        name_converted_order_by = [{**column, 'field': column_ids_to_names[column['field']]} for column in order_by]\n        name_converted_search = [{**column, 'column': column_ids_to_names[column['field']]} for column in search_fuzzy]\n\n        try:\n\n            records = paginator.paginate_queryset(\n                self.get_queryset(), request, table, column_names_to_ids,\n                filters=filter_processed,\n                order_by=name_converted_order_by,\n                grouping=name_converted_group_by,\n                search=name_converted_search,\n                duplicate_only=serializer.validated_data['duplicate_only']\n            )\n        except (BadDBFunctionFormat, UnknownDBFunctionID, ReferencedColumnsDontExist) as e:\n            raise database_api_exceptions.BadFilterAPIException(\n                e,\n                field='filters',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except (BadSortFormat, SortFieldNotFound) as e:\n            raise database_api_exceptions.BadSortAPIException(\n                e,\n                field='order_by',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except (BadGroupFormat, GroupFieldNotFound, InvalidGroupType) as e:\n            raise database_api_exceptions.BadGroupAPIException(\n                e,\n                field='grouping',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except UndefinedFunction as e:\n            raise database_api_exceptions.UndefinedFunctionAPIException(\n                e,\n                details=e.args[0],\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def retrieve(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        # TODO refactor to use serializer for more DRY response logic\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        if not records:\n            raise NotFound\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def create(self, request, table_pk=None):\n        table = get_table_or_404(table_pk)\n        serializer = RecordSerializer(data=request.data, context=self.get_serializer_context(table))\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n        # TODO refactor to use serializer for more DRY response logic\n        column_name_id_map = table.get_column_name_id_bidirectional_map()\n        table_pk_column_id = column_name_id_map[table.primary_key_column_name]\n        pk_value = serializer.data[table_pk_column_id]\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk_value]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        response = paginator.get_paginated_response(serializer.data)\n        response.status_code = status.HTTP_201_CREATED\n        return response\n\n    def partial_update(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        serializer = RecordSerializer(\n            {'id': pk},\n            data=request.data,\n            context=self.get_serializer_context(table),\n            partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n        # TODO refactor to use serializer for more DRY response logic\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def destroy(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        if table.get_record(pk) is None:\n            raise generic_api_exceptions.NotFoundAPIException(\n                NotFound,\n                error_code=ErrorCodes.RecordNotFound.value,\n                message=\"Record doesn't exist\"\n            )\n        try:\n            table.delete_record(pk)\n        except IntegrityError as e:\n            if isinstance(e.orig, ForeignKeyViolation):\n                raise database_api_exceptions.ForeignKeyViolationAPIException(\n                    e,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    referent_table=table,\n                )\n\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    def get_serializer_context(self, table):\n        columns_map = table.get_column_name_id_bidirectional_map()\n        context = {'columns_map': columns_map, 'table': table}\n        return context\n", "path": "mathesar/api/db/viewsets/records.py"}], "after_files": [{"content": "from psycopg2.errors import ForeignKeyViolation, InvalidDatetimeFormat\nfrom rest_access_policy import AccessViewSetMixin\nfrom rest_framework import status, viewsets\nfrom rest_framework.exceptions import NotFound\nfrom rest_framework.renderers import BrowsableAPIRenderer\nfrom rest_framework.response import Response\nfrom sqlalchemy.exc import IntegrityError, DataError\n\nfrom mathesar.api.db.permissions.records import RecordAccessPolicy\nfrom mathesar.api.exceptions.error_codes import ErrorCodes\nimport mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions\nimport mathesar.api.exceptions.generic_exceptions.base_exceptions as generic_api_exceptions\nfrom db.functions.exceptions import (\n    BadDBFunctionFormat, ReferencedColumnsDontExist, UnknownDBFunctionID,\n)\nfrom db.records.exceptions import (\n    BadGroupFormat, GroupFieldNotFound, InvalidGroupType, UndefinedFunction,\n    BadSortFormat, SortFieldNotFound\n)\nfrom mathesar.api.pagination import TableLimitOffsetPagination\nfrom mathesar.api.serializers.records import RecordListParameterSerializer, RecordSerializer\nfrom mathesar.api.utils import get_table_or_404\nfrom mathesar.functions.operations.convert import rewrite_db_function_spec_column_ids_to_names\nfrom mathesar.models.base import Table\nfrom mathesar.utils.json import MathesarJSONRenderer\n\n\nclass RecordViewSet(AccessViewSetMixin, viewsets.ViewSet):\n    access_policy = RecordAccessPolicy\n\n    # There is no 'update' method.\n    # We're not supporting PUT requests because there aren't a lot of use cases\n    # where the entire record needs to be replaced, PATCH suffices for updates.\n    def get_queryset(self):\n        return Table.objects.all().order_by('-created_at')\n\n    renderer_classes = [MathesarJSONRenderer, BrowsableAPIRenderer]\n\n    # For filter parameter formatting, see:\n    # db/functions/operations/deserialize.py::get_db_function_from_ma_function_spec function doc>\n    # For sorting parameter formatting, see:\n    # https://github.com/centerofci/sqlalchemy-filters#sort-format\n    def list(self, request, table_pk=None):\n        paginator = TableLimitOffsetPagination()\n\n        serializer = RecordListParameterSerializer(data=request.GET)\n        serializer.is_valid(raise_exception=True)\n        table = get_table_or_404(table_pk)\n\n        filter_unprocessed = serializer.validated_data['filter']\n        order_by = serializer.validated_data['order_by']\n        grouping = serializer.validated_data['grouping']\n        search_fuzzy = serializer.validated_data['search_fuzzy']\n        filter_processed = None\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        column_ids_to_names = column_names_to_ids.inverse\n        if filter_unprocessed:\n            filter_processed = rewrite_db_function_spec_column_ids_to_names(\n                column_ids_to_names=column_ids_to_names,\n                spec=filter_unprocessed,\n            )\n        # Replace column id value used in the `field` property with column name\n        name_converted_group_by = None\n        if grouping:\n            group_by_columns_names = [column_ids_to_names[column_id] for column_id in grouping['columns']]\n            name_converted_group_by = {**grouping, 'columns': group_by_columns_names}\n        name_converted_order_by = [{**column, 'field': column_ids_to_names[column['field']]} for column in order_by]\n        name_converted_search = [{**column, 'column': column_ids_to_names[column['field']]} for column in search_fuzzy]\n\n        try:\n\n            records = paginator.paginate_queryset(\n                self.get_queryset(), request, table, column_names_to_ids,\n                filters=filter_processed,\n                order_by=name_converted_order_by,\n                grouping=name_converted_group_by,\n                search=name_converted_search,\n                duplicate_only=serializer.validated_data['duplicate_only']\n            )\n        except (BadDBFunctionFormat, UnknownDBFunctionID, ReferencedColumnsDontExist) as e:\n            raise database_api_exceptions.BadFilterAPIException(\n                e,\n                field='filters',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except (BadSortFormat, SortFieldNotFound) as e:\n            raise database_api_exceptions.BadSortAPIException(\n                e,\n                field='order_by',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except (BadGroupFormat, GroupFieldNotFound, InvalidGroupType) as e:\n            raise database_api_exceptions.BadGroupAPIException(\n                e,\n                field='grouping',\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except UndefinedFunction as e:\n            raise database_api_exceptions.UndefinedFunctionAPIException(\n                e,\n                details=e.args[0],\n                status_code=status.HTTP_400_BAD_REQUEST\n            )\n        except DataError as e:\n            if isinstance(e.orig, InvalidDatetimeFormat):\n                raise database_api_exceptions.InvalidDateFormatAPIException(\n                    e,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                )\n\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def retrieve(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        # TODO refactor to use serializer for more DRY response logic\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        if not records:\n            raise NotFound\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def create(self, request, table_pk=None):\n        table = get_table_or_404(table_pk)\n        serializer = RecordSerializer(data=request.data, context=self.get_serializer_context(table))\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n        # TODO refactor to use serializer for more DRY response logic\n        column_name_id_map = table.get_column_name_id_bidirectional_map()\n        table_pk_column_id = column_name_id_map[table.primary_key_column_name]\n        pk_value = serializer.data[table_pk_column_id]\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk_value]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        response = paginator.get_paginated_response(serializer.data)\n        response.status_code = status.HTTP_201_CREATED\n        return response\n\n    def partial_update(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        serializer = RecordSerializer(\n            {'id': pk},\n            data=request.data,\n            context=self.get_serializer_context(table),\n            partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n        serializer.save()\n        # TODO refactor to use serializer for more DRY response logic\n        paginator = TableLimitOffsetPagination()\n        record_filters = {\n            \"equal\": [\n                {\"column_name\": [table.primary_key_column_name]},\n                {\"literal\": [pk]}\n            ]\n        }\n        column_names_to_ids = table.get_column_name_id_bidirectional_map()\n        records = paginator.paginate_queryset(\n            table,\n            request,\n            table,\n            column_names_to_ids,\n            filters=record_filters\n        )\n        serializer = RecordSerializer(\n            records,\n            many=True,\n            context=self.get_serializer_context(table)\n        )\n        return paginator.get_paginated_response(serializer.data)\n\n    def destroy(self, request, pk=None, table_pk=None):\n        table = get_table_or_404(table_pk)\n        if table.get_record(pk) is None:\n            raise generic_api_exceptions.NotFoundAPIException(\n                NotFound,\n                error_code=ErrorCodes.RecordNotFound.value,\n                message=\"Record doesn't exist\"\n            )\n        try:\n            table.delete_record(pk)\n        except IntegrityError as e:\n            if isinstance(e.orig, ForeignKeyViolation):\n                raise database_api_exceptions.ForeignKeyViolationAPIException(\n                    e,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    referent_table=table,\n                )\n\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    def get_serializer_context(self, table):\n        columns_map = table.get_column_name_id_bidirectional_map()\n        context = {'columns_map': columns_map, 'table': table}\n        return context\n", "path": "mathesar/api/db/viewsets/records.py"}]}