prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  API should validate input for number columns
  ## Description
  <!-- A clear and concise description of what the bug is. -->
  
  Currently, the API accepts strings for values input to number-typed columns. In some cases, these strings carry locale-sensitive information, i.e., using specific decimal points and negation styles. This is a problem since confusion will arise whenever the client, service, and database have different locale settings (it's likely the client and DB will have different locale settings by default). Even worse, the locale settings in the database (assuming PostgreSQL) may be applied differently in different contexts.
  
  ## Expected behavior
  <!-- A clear and concise description of what you expected to happen. -->
  
  Columns which use a number type for storage at the DB layer should only accept numbers in one of two formats:
  - an actual JSON number, or
  - A string conforming to the [JSON number spec](https://www.json.org/json-en.html), except wrapped in double-quotes.
  
  The validation of this should be locale-independent, and should happen in the Mathesar web service rather than the database.
  
  ## To Reproduce
  <!-- How can we recreate this bug? Please try to provide a Minimal, Complete, and Verifiable (http://stackoverflow.com/help/mcve) example if code-related. -->
  
  - Create a table with a number-typed column containing a decimal point (e.g., `FLOAT`).
  - Send an API request with input for that column as a string, with a comma for a decimal point. 
      - You can do this easily from the browseable API, see `/api/db/v0/tables/<table_ID>/records/<record_ID>/`
  - Observe the database-layer 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/utils.py`
  Content:
  ```
  1 from rest_framework.exceptions import NotFound
  2 
  3 from db.records.operations import group
  4 from mathesar.models import Table
  5 
  6 DATA_KEY = 'data'
  7 METADATA_KEY = 'metadata'
  8 
  9 
  10 def get_table_or_404(pk):
  11     """
  12     Get table if it exists, otherwise throws a DRF NotFound error.
  13     Args:
  14         pk: id of table
  15     Returns:
  16         table: return the table based on a specific id
  17     """
  18     try:
  19         table = Table.objects.get(id=pk)
  20     except Table.DoesNotExist:
  21         raise NotFound
  22     return table
  23 
  24 
  25 def process_annotated_records(record_list, column_name_id_map):
  26 
  27     RESULT_IDX = 'result_indices'
  28 
  29     def _get_record_dict(record):
  30         return record._asdict() if not isinstance(record, dict) else record
  31 
  32     split_records = (
  33         {DATA_KEY: record_dict}
  34         for record_dict in (_get_record_dict(record) for record in record_list)
  35     )
  36 
  37     combined_records, groups = group.extract_group_metadata(
  38         split_records, data_key=DATA_KEY, metadata_key=METADATA_KEY
  39     )
  40 
  41     processed_records, record_metadata = zip(
  42         *tuple(tuple(d.values()) for d in combined_records)
  43     )
  44 
  45     def _replace_column_names_with_ids(group_metadata_item):
  46         try:
  47             processed_group_metadata_item = {
  48                 column_name_id_map[k]: v for k, v in group_metadata_item.items()
  49             }
  50         except AttributeError:
  51             processed_group_metadata_item = group_metadata_item
  52         return processed_group_metadata_item
  53 
  54     if groups is not None:
  55         groups_by_id = {
  56             grp[group.GroupMetadataField.GROUP_ID.value]: {
  57                 k: _replace_column_names_with_ids(v) for k, v in grp.items()
  58                 if k != group.GroupMetadataField.GROUP_ID.value
  59             } | {RESULT_IDX: []}
  60             for grp in groups
  61         }
  62 
  63         for i, meta in enumerate(record_metadata):
  64             groups_by_id[meta[group.GroupMetadataField.GROUP_ID.value]][RESULT_IDX].append(i)
  65 
  66         output_groups = sorted(list(groups_by_id.values()), key=lambda x: x[RESULT_IDX][0])
  67     else:
  68         output_groups = None
  69 
  70     return processed_records, output_groups
  71
  ```
  
  Path: `mathesar/api/serializers/records.py`
  Content:
  ```
  1 from psycopg2.errors import NotNullViolation
  2 from rest_framework import serializers
  3 from rest_framework import status
  4 from sqlalchemy.exc import IntegrityError
  5 
  6 import mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions
  7 from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin
  8 
  9 
  10 class RecordListParameterSerializer(MathesarErrorMessageMixin, serializers.Serializer):
  11     filter = serializers.JSONField(required=False, default=None)
  12     order_by = serializers.JSONField(required=False, default=[])
  13     grouping = serializers.JSONField(required=False, default={})
  14     duplicate_only = serializers.JSONField(required=False, default=None)
  15 
  16 
  17 class RecordSerializer(MathesarErrorMessageMixin, serializers.BaseSerializer):
  18     def update(self, instance, validated_data):
  19         table = self.context['table']
  20         record = table.update_record(instance['id'], validated_data)
  21         return record
  22 
  23     def create(self, validated_data):
  24         table = self.context['table']
  25         try:
  26             record = table.create_record_or_records(validated_data)
  27         except IntegrityError as e:
  28             if type(e.orig) == NotNullViolation:
  29                 raise database_api_exceptions.NotNullViolationAPIException(
  30                     e,
  31                     status_code=status.HTTP_400_BAD_REQUEST,
  32                     table=table
  33                 )
  34             else:
  35                 raise database_api_exceptions.MathesarAPIException(e, status_code=status.HTTP_400_BAD_REQUEST)
  36         return record
  37 
  38     def to_representation(self, instance):
  39         records = instance._asdict() if not isinstance(instance, dict) else instance
  40         columns_map = self.context['columns_map']
  41         records = {columns_map[column_name]: column_value for column_name, column_value in records.items()}
  42         return records
  43 
  44     def to_internal_value(self, data):
  45         columns_map = self.context['columns_map'].inverse
  46         data = {columns_map[int(column_id)]: value for column_id, value in data.items()}
  47         return data
  48
  ```
  --- 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/serializers/records.py b/mathesar/api/serializers/records.py
  --- a/mathesar/api/serializers/records.py
  +++ b/mathesar/api/serializers/records.py
  @@ -5,6 +5,8 @@
   
   import mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions
   from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin
  +from mathesar.models import Column
  +from mathesar.api.utils import follows_json_number_spec, is_number
   
   
   class RecordListParameterSerializer(MathesarErrorMessageMixin, serializers.Serializer):
  @@ -44,4 +46,17 @@
       def to_internal_value(self, data):
           columns_map = self.context['columns_map'].inverse
           data = {columns_map[int(column_id)]: value for column_id, value in data.items()}
  +        # If the data type of the column is number then the value must be an integer
  +        # or a string which follows JSON number spec.
  +        for column_name in data.keys():
  +            column = Column.objects.get(id=columns_map.inverse[column_name])
  +            column_type = column.type
  +            value = data[column_name]
  +            if is_number(column_type) and type(data[column_name]) is str and not follows_json_number_spec(value):
  +                raise database_api_exceptions.MathesarAPIException(
  +                    IntegrityError,
  +                    status_code=status.HTTP_400_BAD_REQUEST,
  +                    message="Number strings should follow JSON number spec",
  +                    field=column_name
  +                )
           return data
  diff --git a/mathesar/api/utils.py b/mathesar/api/utils.py
  --- a/mathesar/api/utils.py
  +++ b/mathesar/api/utils.py
  @@ -1,7 +1,9 @@
   from rest_framework.exceptions import NotFound
  +import re
   
   from db.records.operations import group
   from mathesar.models import Table
  +from mathesar.database.types import _get_type_map
   
   DATA_KEY = 'data'
   METADATA_KEY = 'metadata'
  @@ -68,3 +70,39 @@
           output_groups = None
   
       return processed_records, output_groups
  +
  +
  +def is_number(column_type):
  +    """
  +    Check if a column data type is a number
  +    Args:
  +        column_type: data type of column
  +    """
  +    for type in _get_type_map():
  +        if type['name'] == 'Number':
  +            if str(column_type).lower() in type['sa_type_names']:
  +                return True
  +            else:
  +                return False
  +
  +
  +def follows_json_number_spec(number):
  +    """
  +    Check if a string follows JSON number spec
  +    Args:
  +        number: number as string
  +    """
  +    patterns = [
  +        r"^-?0$",
  +        r"^-?0[\.][0-9]+$",
  +        r"^-?0[eE][+-]?[0-9]*$",
  +        r"^-?0[\.][0-9]+[eE][+-]?[0-9]+$",
  +        r"^-?[1-9][0-9]*$",
  +        r"^-?[1-9][0-9]*[\.][0-9]+$",
  +        r"^-?[1-9][0-9]*[eE][+-]?[0-9]+$",
  +        r"^-?[1-9][0-9]*[\.][0-9]+[eE][+-]?[0-9]+$",
  +    ]
  +    for pattern in patterns:
  +        if re.search(pattern, number) is not None:
  +            return True
  +    return False
  
verification_info: {"golden_diff": "diff --git a/mathesar/api/serializers/records.py b/mathesar/api/serializers/records.py\n--- a/mathesar/api/serializers/records.py\n+++ b/mathesar/api/serializers/records.py\n@@ -5,6 +5,8 @@\n \n import mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions\n from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\n+from mathesar.models import Column\n+from mathesar.api.utils import follows_json_number_spec, is_number\n \n \n class RecordListParameterSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n@@ -44,4 +46,17 @@\n     def to_internal_value(self, data):\n         columns_map = self.context['columns_map'].inverse\n         data = {columns_map[int(column_id)]: value for column_id, value in data.items()}\n+        # If the data type of the column is number then the value must be an integer\n+        # or a string which follows JSON number spec.\n+        for column_name in data.keys():\n+            column = Column.objects.get(id=columns_map.inverse[column_name])\n+            column_type = column.type\n+            value = data[column_name]\n+            if is_number(column_type) and type(data[column_name]) is str and not follows_json_number_spec(value):\n+                raise database_api_exceptions.MathesarAPIException(\n+                    IntegrityError,\n+                    status_code=status.HTTP_400_BAD_REQUEST,\n+                    message=\"Number strings should follow JSON number spec\",\n+                    field=column_name\n+                )\n         return data\ndiff --git a/mathesar/api/utils.py b/mathesar/api/utils.py\n--- a/mathesar/api/utils.py\n+++ b/mathesar/api/utils.py\n@@ -1,7 +1,9 @@\n from rest_framework.exceptions import NotFound\n+import re\n \n from db.records.operations import group\n from mathesar.models import Table\n+from mathesar.database.types import _get_type_map\n \n DATA_KEY = 'data'\n METADATA_KEY = 'metadata'\n@@ -68,3 +70,39 @@\n         output_groups = None\n \n     return processed_records, output_groups\n+\n+\n+def is_number(column_type):\n+    \"\"\"\n+    Check if a column data type is a number\n+    Args:\n+        column_type: data type of column\n+    \"\"\"\n+    for type in _get_type_map():\n+        if type['name'] == 'Number':\n+            if str(column_type).lower() in type['sa_type_names']:\n+                return True\n+            else:\n+                return False\n+\n+\n+def follows_json_number_spec(number):\n+    \"\"\"\n+    Check if a string follows JSON number spec\n+    Args:\n+        number: number as string\n+    \"\"\"\n+    patterns = [\n+        r\"^-?0$\",\n+        r\"^-?0[\\.][0-9]+$\",\n+        r\"^-?0[eE][+-]?[0-9]*$\",\n+        r\"^-?0[\\.][0-9]+[eE][+-]?[0-9]+$\",\n+        r\"^-?[1-9][0-9]*$\",\n+        r\"^-?[1-9][0-9]*[\\.][0-9]+$\",\n+        r\"^-?[1-9][0-9]*[eE][+-]?[0-9]+$\",\n+        r\"^-?[1-9][0-9]*[\\.][0-9]+[eE][+-]?[0-9]+$\",\n+    ]\n+    for pattern in patterns:\n+        if re.search(pattern, number) is not None:\n+            return True\n+    return False\n", "issue": "API should validate input for number columns\n## Description\r\n<!-- A clear and concise description of what the bug is. -->\r\n\r\nCurrently, the API accepts strings for values input to number-typed columns. In some cases, these strings carry locale-sensitive information, i.e., using specific decimal points and negation styles. This is a problem since confusion will arise whenever the client, service, and database have different locale settings (it's likely the client and DB will have different locale settings by default). Even worse, the locale settings in the database (assuming PostgreSQL) may be applied differently in different contexts.\r\n\r\n## Expected behavior\r\n<!-- A clear and concise description of what you expected to happen. -->\r\n\r\nColumns which use a number type for storage at the DB layer should only accept numbers in one of two formats:\r\n- an actual JSON number, or\r\n- A string conforming to the [JSON number spec](https://www.json.org/json-en.html), except wrapped in double-quotes.\r\n\r\nThe validation of this should be locale-independent, and should happen in the Mathesar web service rather than the database.\r\n\r\n## To Reproduce\r\n<!-- How can we recreate this bug? Please try to provide a Minimal, Complete, and Verifiable (http://stackoverflow.com/help/mcve) example if code-related. -->\r\n\r\n- Create a table with a number-typed column containing a decimal point (e.g., `FLOAT`).\r\n- Send an API request with input for that column as a string, with a comma for a decimal point. \r\n    - You can do this easily from the browseable API, see `/api/db/v0/tables/<table_ID>/records/<record_ID>/`\r\n- Observe the database-layer error.\r\n\n", "before_files": [{"content": "from rest_framework.exceptions import NotFound\n\nfrom db.records.operations import group\nfrom mathesar.models import Table\n\nDATA_KEY = 'data'\nMETADATA_KEY = 'metadata'\n\n\ndef get_table_or_404(pk):\n    \"\"\"\n    Get table if it exists, otherwise throws a DRF NotFound error.\n    Args:\n        pk: id of table\n    Returns:\n        table: return the table based on a specific id\n    \"\"\"\n    try:\n        table = Table.objects.get(id=pk)\n    except Table.DoesNotExist:\n        raise NotFound\n    return table\n\n\ndef process_annotated_records(record_list, column_name_id_map):\n\n    RESULT_IDX = 'result_indices'\n\n    def _get_record_dict(record):\n        return record._asdict() if not isinstance(record, dict) else record\n\n    split_records = (\n        {DATA_KEY: record_dict}\n        for record_dict in (_get_record_dict(record) for record in record_list)\n    )\n\n    combined_records, groups = group.extract_group_metadata(\n        split_records, data_key=DATA_KEY, metadata_key=METADATA_KEY\n    )\n\n    processed_records, record_metadata = zip(\n        *tuple(tuple(d.values()) for d in combined_records)\n    )\n\n    def _replace_column_names_with_ids(group_metadata_item):\n        try:\n            processed_group_metadata_item = {\n                column_name_id_map[k]: v for k, v in group_metadata_item.items()\n            }\n        except AttributeError:\n            processed_group_metadata_item = group_metadata_item\n        return processed_group_metadata_item\n\n    if groups is not None:\n        groups_by_id = {\n            grp[group.GroupMetadataField.GROUP_ID.value]: {\n                k: _replace_column_names_with_ids(v) for k, v in grp.items()\n                if k != group.GroupMetadataField.GROUP_ID.value\n            } | {RESULT_IDX: []}\n            for grp in groups\n        }\n\n        for i, meta in enumerate(record_metadata):\n            groups_by_id[meta[group.GroupMetadataField.GROUP_ID.value]][RESULT_IDX].append(i)\n\n        output_groups = sorted(list(groups_by_id.values()), key=lambda x: x[RESULT_IDX][0])\n    else:\n        output_groups = None\n\n    return processed_records, output_groups\n", "path": "mathesar/api/utils.py"}, {"content": "from psycopg2.errors import NotNullViolation\nfrom rest_framework import serializers\nfrom rest_framework import status\nfrom sqlalchemy.exc import IntegrityError\n\nimport mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\n\n\nclass RecordListParameterSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    filter = serializers.JSONField(required=False, default=None)\n    order_by = serializers.JSONField(required=False, default=[])\n    grouping = serializers.JSONField(required=False, default={})\n    duplicate_only = serializers.JSONField(required=False, default=None)\n\n\nclass RecordSerializer(MathesarErrorMessageMixin, serializers.BaseSerializer):\n    def update(self, instance, validated_data):\n        table = self.context['table']\n        record = table.update_record(instance['id'], validated_data)\n        return record\n\n    def create(self, validated_data):\n        table = self.context['table']\n        try:\n            record = table.create_record_or_records(validated_data)\n        except IntegrityError as e:\n            if type(e.orig) == NotNullViolation:\n                raise database_api_exceptions.NotNullViolationAPIException(\n                    e,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    table=table\n                )\n            else:\n                raise database_api_exceptions.MathesarAPIException(e, status_code=status.HTTP_400_BAD_REQUEST)\n        return record\n\n    def to_representation(self, instance):\n        records = instance._asdict() if not isinstance(instance, dict) else instance\n        columns_map = self.context['columns_map']\n        records = {columns_map[column_name]: column_value for column_name, column_value in records.items()}\n        return records\n\n    def to_internal_value(self, data):\n        columns_map = self.context['columns_map'].inverse\n        data = {columns_map[int(column_id)]: value for column_id, value in data.items()}\n        return data\n", "path": "mathesar/api/serializers/records.py"}], "after_files": [{"content": "from rest_framework.exceptions import NotFound\nimport re\n\nfrom db.records.operations import group\nfrom mathesar.models import Table\nfrom mathesar.database.types import _get_type_map\n\nDATA_KEY = 'data'\nMETADATA_KEY = 'metadata'\n\n\ndef get_table_or_404(pk):\n    \"\"\"\n    Get table if it exists, otherwise throws a DRF NotFound error.\n    Args:\n        pk: id of table\n    Returns:\n        table: return the table based on a specific id\n    \"\"\"\n    try:\n        table = Table.objects.get(id=pk)\n    except Table.DoesNotExist:\n        raise NotFound\n    return table\n\n\ndef process_annotated_records(record_list, column_name_id_map):\n\n    RESULT_IDX = 'result_indices'\n\n    def _get_record_dict(record):\n        return record._asdict() if not isinstance(record, dict) else record\n\n    split_records = (\n        {DATA_KEY: record_dict}\n        for record_dict in (_get_record_dict(record) for record in record_list)\n    )\n\n    combined_records, groups = group.extract_group_metadata(\n        split_records, data_key=DATA_KEY, metadata_key=METADATA_KEY\n    )\n\n    processed_records, record_metadata = zip(\n        *tuple(tuple(d.values()) for d in combined_records)\n    )\n\n    def _replace_column_names_with_ids(group_metadata_item):\n        try:\n            processed_group_metadata_item = {\n                column_name_id_map[k]: v for k, v in group_metadata_item.items()\n            }\n        except AttributeError:\n            processed_group_metadata_item = group_metadata_item\n        return processed_group_metadata_item\n\n    if groups is not None:\n        groups_by_id = {\n            grp[group.GroupMetadataField.GROUP_ID.value]: {\n                k: _replace_column_names_with_ids(v) for k, v in grp.items()\n                if k != group.GroupMetadataField.GROUP_ID.value\n            } | {RESULT_IDX: []}\n            for grp in groups\n        }\n\n        for i, meta in enumerate(record_metadata):\n            groups_by_id[meta[group.GroupMetadataField.GROUP_ID.value]][RESULT_IDX].append(i)\n\n        output_groups = sorted(list(groups_by_id.values()), key=lambda x: x[RESULT_IDX][0])\n    else:\n        output_groups = None\n\n    return processed_records, output_groups\n\n\ndef is_number(column_type):\n    \"\"\"\n    Check if a column data type is a number\n    Args:\n        column_type: data type of column\n    \"\"\"\n    for type in _get_type_map():\n        if type['name'] == 'Number':\n            if str(column_type).lower() in type['sa_type_names']:\n                return True\n            else:\n                return False\n\n\ndef follows_json_number_spec(number):\n    \"\"\"\n    Check if a string follows JSON number spec\n    Args:\n        number: number as string\n    \"\"\"\n    patterns = [\n        r\"^-?0$\",\n        r\"^-?0[\\.][0-9]+$\",\n        r\"^-?0[eE][+-]?[0-9]*$\",\n        r\"^-?0[\\.][0-9]+[eE][+-]?[0-9]+$\",\n        r\"^-?[1-9][0-9]*$\",\n        r\"^-?[1-9][0-9]*[\\.][0-9]+$\",\n        r\"^-?[1-9][0-9]*[eE][+-]?[0-9]+$\",\n        r\"^-?[1-9][0-9]*[\\.][0-9]+[eE][+-]?[0-9]+$\",\n    ]\n    for pattern in patterns:\n        if re.search(pattern, number) is not None:\n            return True\n    return False\n", "path": "mathesar/api/utils.py"}, {"content": "from psycopg2.errors import NotNullViolation\nfrom rest_framework import serializers\nfrom rest_framework import status\nfrom sqlalchemy.exc import IntegrityError\n\nimport mathesar.api.exceptions.database_exceptions.exceptions as database_api_exceptions\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\nfrom mathesar.models import Column\nfrom mathesar.api.utils import follows_json_number_spec, is_number\n\n\nclass RecordListParameterSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    filter = serializers.JSONField(required=False, default=None)\n    order_by = serializers.JSONField(required=False, default=[])\n    grouping = serializers.JSONField(required=False, default={})\n    duplicate_only = serializers.JSONField(required=False, default=None)\n\n\nclass RecordSerializer(MathesarErrorMessageMixin, serializers.BaseSerializer):\n    def update(self, instance, validated_data):\n        table = self.context['table']\n        record = table.update_record(instance['id'], validated_data)\n        return record\n\n    def create(self, validated_data):\n        table = self.context['table']\n        try:\n            record = table.create_record_or_records(validated_data)\n        except IntegrityError as e:\n            if type(e.orig) == NotNullViolation:\n                raise database_api_exceptions.NotNullViolationAPIException(\n                    e,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    table=table\n                )\n            else:\n                raise database_api_exceptions.MathesarAPIException(e, status_code=status.HTTP_400_BAD_REQUEST)\n        return record\n\n    def to_representation(self, instance):\n        records = instance._asdict() if not isinstance(instance, dict) else instance\n        columns_map = self.context['columns_map']\n        records = {columns_map[column_name]: column_value for column_name, column_value in records.items()}\n        return records\n\n    def to_internal_value(self, data):\n        columns_map = self.context['columns_map'].inverse\n        data = {columns_map[int(column_id)]: value for column_id, value in data.items()}\n        # If the data type of the column is number then the value must be an integer\n        # or a string which follows JSON number spec.\n        for column_name in data.keys():\n            column = Column.objects.get(id=columns_map.inverse[column_name])\n            column_type = column.type\n            value = data[column_name]\n            if is_number(column_type) and type(data[column_name]) is str and not follows_json_number_spec(value):\n                raise database_api_exceptions.MathesarAPIException(\n                    IntegrityError,\n                    status_code=status.HTTP_400_BAD_REQUEST,\n                    message=\"Number strings should follow JSON number spec\",\n                    field=column_name\n                )\n        return data\n", "path": "mathesar/api/serializers/records.py"}]}