We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Typecating column to JSON_List/Map doesn't work.
## 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. -->

1. Create an empty table.
2. Create a new column and add a JSON array to it.
3.  Try to typecast the column to JSON List.
4. Notice there is no error/change to the data type of the column.


## Additional context
<!-- Add any other context about the problem or screenshots here. -->
![Screenshot from 2022-11-19 22-09-51](https://user-images.githubusercontent.com/52523023/202862061-f447aeb4-9af0-4054-b398-f3288ba50a5c.png)


--- 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/serializers/columns.py`
Content:
```
1 from rest_framework import serializers
2 from rest_framework.exceptions import ValidationError
3 from rest_framework.fields import empty, SerializerMethodField
4 from rest_framework.settings import api_settings
5 
6 from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin
7 from mathesar.api.serializers.shared_serializers import (
8     DisplayOptionsMappingSerializer,
9     DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY,
10 )
11 from mathesar.models.base import Column
12 from db.types.operations.convert import get_db_type_enum_from_id
13 
14 
15 class InputValueField(serializers.CharField):
16     """
17     Takes in an arbitrary value. Emulates the record creation endpoint,
18     which takes in arbitrary values (un-validated and un-processed request.data).
19     This field replicates that behavior in a serializer.
20     """
21 
22     def to_internal_value(self, data):
23         return data
24 
25     def to_representation(self, value):
26         return value
27 
28 
29 class TypeOptionSerializer(MathesarErrorMessageMixin, serializers.Serializer):
30     length = serializers.IntegerField(required=False)
31     precision = serializers.IntegerField(required=False)
32     scale = serializers.IntegerField(required=False)
33     fields = serializers.CharField(required=False)
34 
35     def validate(self, attrs):
36         if attrs.get('scale', None) is not None and attrs.get('precision', None) is None:
37             attrs['precision'] = 1000
38         return super().validate(attrs)
39 
40     def run_validation(self, data=empty):
41         # Ensure that there are no unknown type options passed in.
42         if data is not empty and data is not None:
43             unknown = set(data) - set(self.fields)
44             if unknown:
45                 errors = ['Unknown field: {}'.format(field) for field in unknown]
46                 raise serializers.ValidationError({
47                     api_settings.NON_FIELD_ERRORS_KEY: errors,
48                 })
49 
50         return super(TypeOptionSerializer, self).run_validation(data)
51 
52 
53 TYPE_KEY = 'type'
54 DISPLAY_OPTIONS_KEY = 'display_options'
55 
56 
57 class SimpleColumnSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
58     class Meta:
59         model = Column
60         fields = ('id',
61                   'name',
62                   TYPE_KEY,
63                   'type_options',
64                   DISPLAY_OPTIONS_KEY,
65                   )
66     id = serializers.IntegerField(required=False)
67     name = serializers.CharField()
68     # TODO consider renaming type and type_options to db_type and db_type_options
69     # The name of below attribute should match value of TYPE_KEY
70     type = serializers.CharField()
71     type_options = TypeOptionSerializer(required=False, allow_null=True)
72     # The name of below attribute should match value of DISPLAY_OPTIONS_KEY
73     display_options = DisplayOptionsMappingSerializer(required=False, allow_null=True)
74 
75     def to_representation(self, instance):
76         if isinstance(instance, dict):
77             db_type_id = instance.get(TYPE_KEY)
78             db_type = get_db_type_enum_from_id(db_type_id)
79         else:
80             db_type = instance.db_type
81         # TODO replace or remove this assert before production
82         assert db_type is not None
83         self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type
84         representation = super().to_representation(instance)
85         _force_canonical_type(representation, db_type)
86         return representation
87 
88     def to_internal_value(self, data):
89         if self.partial and TYPE_KEY not in data:
90             db_type = getattr(self.instance, 'db_type', None)
91         else:
92             db_type_id = data.get(TYPE_KEY, None)
93             db_type = get_db_type_enum_from_id(db_type_id) if db_type_id else None
94         self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type
95         return super().to_internal_value(data)
96 
97 
98 def _force_canonical_type(representation, db_type):
99     """
100     Sometimes the representation's TYPE_KEY attribute will also include type option information
101     (e.g. `numeric(3, 5)`). We override the attribute's value to a canonical type id.
102 
103     This might be better solved upstream, but since our Column model subclasses SA's Column,
104     overriding its TYPE_KEY attribute, might interfere with SA's workings.
105     """
106     representation[TYPE_KEY] = db_type.id
107     return representation
108 
109 
110 class ColumnDefaultSerializer(MathesarErrorMessageMixin, serializers.Serializer):
111     value = InputValueField()
112     is_dynamic = serializers.BooleanField(read_only=True)
113 
114 
115 class ColumnSerializer(SimpleColumnSerializer):
116     class Meta(SimpleColumnSerializer.Meta):
117         fields = SimpleColumnSerializer.Meta.fields + (
118             'nullable',
119             'primary_key',
120             'source_column',
121             'copy_source_data',
122             'copy_source_constraints',
123             'valid_target_types',
124             'default',
125             'has_dependents',
126         )
127         model_fields = (DISPLAY_OPTIONS_KEY,)
128 
129     name = serializers.CharField(required=False, allow_blank=True)
130 
131     # From scratch fields
132     type = serializers.CharField(required=False)
133     nullable = serializers.BooleanField(default=True)
134     primary_key = serializers.BooleanField(default=False)
135     default = ColumnDefaultSerializer(
136         source='column_default_dict', required=False, allow_null=True, default=None
137     )
138 
139     # From duplication fields
140     source_column = serializers.PrimaryKeyRelatedField(queryset=Column.current_objects.all(), required=False, write_only=True)
141     copy_source_data = serializers.BooleanField(default=True, write_only=True)
142     copy_source_constraints = serializers.BooleanField(default=True, write_only=True)
143 
144     # Read only fields
145     valid_target_types = SerializerMethodField(method_name='get_valid_target_types', read_only=True)
146 
147     def validate(self, data):
148         data = super().validate(data)
149         # Reevaluate column display options based on the new column type.
150         if TYPE_KEY in data and DISPLAY_OPTIONS_KEY not in data:
151             if self.instance:
152                 db_type = getattr(self.instance, 'db_type', None)
153                 # Invalidate display_options if type has been changed
154                 if db_type is not None:
155                     if str(db_type.id) != data[TYPE_KEY]:
156                         data[DISPLAY_OPTIONS_KEY] = None
157             else:
158                 data[DISPLAY_OPTIONS_KEY] = None
159         if not self.partial:
160             from_scratch_required_fields = [TYPE_KEY]
161             from_scratch_specific_fields = [TYPE_KEY, 'nullable', 'primary_key']
162             from_dupe_required_fields = ['source_column']
163             from_dupe_specific_fields = ['source_column', 'copy_source_data',
164                                          'copy_source_constraints']
165 
166             # Note that we run validation on self.initial_data, as `data` has defaults
167             # filled in for fields that weren't specified by the request
168             from_scratch_required_all = all([
169                 f in self.initial_data for f in from_scratch_required_fields
170             ])
171             from_scratch_specific_in = [
172                 f for f in from_scratch_specific_fields if f in self.initial_data
173             ]
174             from_dupe_required_all = all([
175                 f in self.initial_data for f in from_dupe_required_fields
176             ])
177             from_dupe_specific_in = [
178                 f for f in from_dupe_specific_fields if f in self.initial_data
179             ]
180 
181             if len(from_dupe_specific_in) and len(from_scratch_specific_in):
182                 raise ValidationError(
183                     f'{from_scratch_specific_in} cannot be passed in if '
184                     f'{from_dupe_specific_in} has also been passed in.'
185                 )
186             elif not from_dupe_required_all and not from_scratch_required_all:
187                 # We default to from scratch required fields if no fields are passed
188                 if len(from_dupe_specific_in) and not len(from_scratch_specific_in):
189                     required_fields = from_dupe_required_fields
190                 else:
191                     required_fields = from_scratch_required_fields
192                 raise ValidationError({
193                     f: ['This field is required.']
194                     for f in required_fields
195                     if f not in self.initial_data
196                 })
197         return data
198 
199     @property
200     def validated_model_fields(self):
201         return {key: self.validated_data[key] for key in self.validated_data if key in self.Meta.model_fields}
202 
203     def get_valid_target_types(self, column):
204         valid_target_types = column.valid_target_types
205         if valid_target_types:
206             valid_target_type_ids = tuple(
207                 db_type.id for db_type in valid_target_types
208             )
209             return valid_target_type_ids
210
```
--- 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/serializers/columns.py b/mathesar/api/serializers/columns.py
--- a/mathesar/api/serializers/columns.py
+++ b/mathesar/api/serializers/columns.py
@@ -1,4 +1,4 @@
-from rest_framework import serializers
+from rest_framework import serializers, status
 from rest_framework.exceptions import ValidationError
 from rest_framework.fields import empty, SerializerMethodField
 from rest_framework.settings import api_settings
@@ -8,6 +8,10 @@
     DisplayOptionsMappingSerializer,
     DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY,
 )
+from mathesar.api.exceptions.database_exceptions import (
+    exceptions as database_api_exceptions,
+)
+from db.columns.exceptions import InvalidTypeError
 from mathesar.models.base import Column
 from db.types.operations.convert import get_db_type_enum_from_id
 
@@ -147,15 +151,20 @@
     def validate(self, data):
         data = super().validate(data)
         # Reevaluate column display options based on the new column type.
-        if TYPE_KEY in data and DISPLAY_OPTIONS_KEY not in data:
-            if self.instance:
+        if TYPE_KEY in data and self.instance:
+            db_type = get_db_type_enum_from_id(data[TYPE_KEY].lower())
+            target_types = self.instance.valid_target_types
+            if db_type not in target_types:
+                raise database_api_exceptions.InvalidTypeCastAPIException(
+                    InvalidTypeError,
+                    status_code=status.HTTP_400_BAD_REQUEST
+                )
+            if DISPLAY_OPTIONS_KEY not in data:
                 db_type = getattr(self.instance, 'db_type', None)
                 # Invalidate display_options if type has been changed
                 if db_type is not None:
                     if str(db_type.id) != data[TYPE_KEY]:
                         data[DISPLAY_OPTIONS_KEY] = None
-            else:
-                data[DISPLAY_OPTIONS_KEY] = None
         if not self.partial:
             from_scratch_required_fields = [TYPE_KEY]
             from_scratch_specific_fields = [TYPE_KEY, 'nullable', 'primary_key']

{"golden_diff": "diff --git a/mathesar/api/serializers/columns.py b/mathesar/api/serializers/columns.py\n--- a/mathesar/api/serializers/columns.py\n+++ b/mathesar/api/serializers/columns.py\n@@ -1,4 +1,4 @@\n-from rest_framework import serializers\n+from rest_framework import serializers, status\n from rest_framework.exceptions import ValidationError\n from rest_framework.fields import empty, SerializerMethodField\n from rest_framework.settings import api_settings\n@@ -8,6 +8,10 @@\n     DisplayOptionsMappingSerializer,\n     DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY,\n )\n+from mathesar.api.exceptions.database_exceptions import (\n+    exceptions as database_api_exceptions,\n+)\n+from db.columns.exceptions import InvalidTypeError\n from mathesar.models.base import Column\n from db.types.operations.convert import get_db_type_enum_from_id\n \n@@ -147,15 +151,20 @@\n     def validate(self, data):\n         data = super().validate(data)\n         # Reevaluate column display options based on the new column type.\n-        if TYPE_KEY in data and DISPLAY_OPTIONS_KEY not in data:\n-            if self.instance:\n+        if TYPE_KEY in data and self.instance:\n+            db_type = get_db_type_enum_from_id(data[TYPE_KEY].lower())\n+            target_types = self.instance.valid_target_types\n+            if db_type not in target_types:\n+                raise database_api_exceptions.InvalidTypeCastAPIException(\n+                    InvalidTypeError,\n+                    status_code=status.HTTP_400_BAD_REQUEST\n+                )\n+            if DISPLAY_OPTIONS_KEY not in data:\n                 db_type = getattr(self.instance, 'db_type', None)\n                 # Invalidate display_options if type has been changed\n                 if db_type is not None:\n                     if str(db_type.id) != data[TYPE_KEY]:\n                         data[DISPLAY_OPTIONS_KEY] = None\n-            else:\n-                data[DISPLAY_OPTIONS_KEY] = None\n         if not self.partial:\n             from_scratch_required_fields = [TYPE_KEY]\n             from_scratch_specific_fields = [TYPE_KEY, 'nullable', 'primary_key']\n", "issue": "Typecating column to JSON_List/Map doesn't work.\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\n1. Create an empty table.\r\n2. Create a new column and add a JSON array to it.\r\n3.  Try to typecast the column to JSON List.\r\n4. Notice there is no error/change to the data type of the column.\r\n\r\n\r\n## Additional context\r\n<!-- Add any other context about the problem or screenshots here. -->\r\n![Screenshot from 2022-11-19 22-09-51](https://user-images.githubusercontent.com/52523023/202862061-f447aeb4-9af0-4054-b398-f3288ba50a5c.png)\r\n\n", "before_files": [{"content": "from rest_framework import serializers\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import empty, SerializerMethodField\nfrom rest_framework.settings import api_settings\n\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\nfrom mathesar.api.serializers.shared_serializers import (\n    DisplayOptionsMappingSerializer,\n    DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY,\n)\nfrom mathesar.models.base import Column\nfrom db.types.operations.convert import get_db_type_enum_from_id\n\n\nclass InputValueField(serializers.CharField):\n    \"\"\"\n    Takes in an arbitrary value. Emulates the record creation endpoint,\n    which takes in arbitrary values (un-validated and un-processed request.data).\n    This field replicates that behavior in a serializer.\n    \"\"\"\n\n    def to_internal_value(self, data):\n        return data\n\n    def to_representation(self, value):\n        return value\n\n\nclass TypeOptionSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    length = serializers.IntegerField(required=False)\n    precision = serializers.IntegerField(required=False)\n    scale = serializers.IntegerField(required=False)\n    fields = serializers.CharField(required=False)\n\n    def validate(self, attrs):\n        if attrs.get('scale', None) is not None and attrs.get('precision', None) is None:\n            attrs['precision'] = 1000\n        return super().validate(attrs)\n\n    def run_validation(self, data=empty):\n        # Ensure that there are no unknown type options passed in.\n        if data is not empty and data is not None:\n            unknown = set(data) - set(self.fields)\n            if unknown:\n                errors = ['Unknown field: {}'.format(field) for field in unknown]\n                raise serializers.ValidationError({\n                    api_settings.NON_FIELD_ERRORS_KEY: errors,\n                })\n\n        return super(TypeOptionSerializer, self).run_validation(data)\n\n\nTYPE_KEY = 'type'\nDISPLAY_OPTIONS_KEY = 'display_options'\n\n\nclass SimpleColumnSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = Column\n        fields = ('id',\n                  'name',\n                  TYPE_KEY,\n                  'type_options',\n                  DISPLAY_OPTIONS_KEY,\n                  )\n    id = serializers.IntegerField(required=False)\n    name = serializers.CharField()\n    # TODO consider renaming type and type_options to db_type and db_type_options\n    # The name of below attribute should match value of TYPE_KEY\n    type = serializers.CharField()\n    type_options = TypeOptionSerializer(required=False, allow_null=True)\n    # The name of below attribute should match value of DISPLAY_OPTIONS_KEY\n    display_options = DisplayOptionsMappingSerializer(required=False, allow_null=True)\n\n    def to_representation(self, instance):\n        if isinstance(instance, dict):\n            db_type_id = instance.get(TYPE_KEY)\n            db_type = get_db_type_enum_from_id(db_type_id)\n        else:\n            db_type = instance.db_type\n        # TODO replace or remove this assert before production\n        assert db_type is not None\n        self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type\n        representation = super().to_representation(instance)\n        _force_canonical_type(representation, db_type)\n        return representation\n\n    def to_internal_value(self, data):\n        if self.partial and TYPE_KEY not in data:\n            db_type = getattr(self.instance, 'db_type', None)\n        else:\n            db_type_id = data.get(TYPE_KEY, None)\n            db_type = get_db_type_enum_from_id(db_type_id) if db_type_id else None\n        self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type\n        return super().to_internal_value(data)\n\n\ndef _force_canonical_type(representation, db_type):\n    \"\"\"\n    Sometimes the representation's TYPE_KEY attribute will also include type option information\n    (e.g. `numeric(3, 5)`). We override the attribute's value to a canonical type id.\n\n    This might be better solved upstream, but since our Column model subclasses SA's Column,\n    overriding its TYPE_KEY attribute, might interfere with SA's workings.\n    \"\"\"\n    representation[TYPE_KEY] = db_type.id\n    return representation\n\n\nclass ColumnDefaultSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    value = InputValueField()\n    is_dynamic = serializers.BooleanField(read_only=True)\n\n\nclass ColumnSerializer(SimpleColumnSerializer):\n    class Meta(SimpleColumnSerializer.Meta):\n        fields = SimpleColumnSerializer.Meta.fields + (\n            'nullable',\n            'primary_key',\n            'source_column',\n            'copy_source_data',\n            'copy_source_constraints',\n            'valid_target_types',\n            'default',\n            'has_dependents',\n        )\n        model_fields = (DISPLAY_OPTIONS_KEY,)\n\n    name = serializers.CharField(required=False, allow_blank=True)\n\n    # From scratch fields\n    type = serializers.CharField(required=False)\n    nullable = serializers.BooleanField(default=True)\n    primary_key = serializers.BooleanField(default=False)\n    default = ColumnDefaultSerializer(\n        source='column_default_dict', required=False, allow_null=True, default=None\n    )\n\n    # From duplication fields\n    source_column = serializers.PrimaryKeyRelatedField(queryset=Column.current_objects.all(), required=False, write_only=True)\n    copy_source_data = serializers.BooleanField(default=True, write_only=True)\n    copy_source_constraints = serializers.BooleanField(default=True, write_only=True)\n\n    # Read only fields\n    valid_target_types = SerializerMethodField(method_name='get_valid_target_types', read_only=True)\n\n    def validate(self, data):\n        data = super().validate(data)\n        # Reevaluate column display options based on the new column type.\n        if TYPE_KEY in data and DISPLAY_OPTIONS_KEY not in data:\n            if self.instance:\n                db_type = getattr(self.instance, 'db_type', None)\n                # Invalidate display_options if type has been changed\n                if db_type is not None:\n                    if str(db_type.id) != data[TYPE_KEY]:\n                        data[DISPLAY_OPTIONS_KEY] = None\n            else:\n                data[DISPLAY_OPTIONS_KEY] = None\n        if not self.partial:\n            from_scratch_required_fields = [TYPE_KEY]\n            from_scratch_specific_fields = [TYPE_KEY, 'nullable', 'primary_key']\n            from_dupe_required_fields = ['source_column']\n            from_dupe_specific_fields = ['source_column', 'copy_source_data',\n                                         'copy_source_constraints']\n\n            # Note that we run validation on self.initial_data, as `data` has defaults\n            # filled in for fields that weren't specified by the request\n            from_scratch_required_all = all([\n                f in self.initial_data for f in from_scratch_required_fields\n            ])\n            from_scratch_specific_in = [\n                f for f in from_scratch_specific_fields if f in self.initial_data\n            ]\n            from_dupe_required_all = all([\n                f in self.initial_data for f in from_dupe_required_fields\n            ])\n            from_dupe_specific_in = [\n                f for f in from_dupe_specific_fields if f in self.initial_data\n            ]\n\n            if len(from_dupe_specific_in) and len(from_scratch_specific_in):\n                raise ValidationError(\n                    f'{from_scratch_specific_in} cannot be passed in if '\n                    f'{from_dupe_specific_in} has also been passed in.'\n                )\n            elif not from_dupe_required_all and not from_scratch_required_all:\n                # We default to from scratch required fields if no fields are passed\n                if len(from_dupe_specific_in) and not len(from_scratch_specific_in):\n                    required_fields = from_dupe_required_fields\n                else:\n                    required_fields = from_scratch_required_fields\n                raise ValidationError({\n                    f: ['This field is required.']\n                    for f in required_fields\n                    if f not in self.initial_data\n                })\n        return data\n\n    @property\n    def validated_model_fields(self):\n        return {key: self.validated_data[key] for key in self.validated_data if key in self.Meta.model_fields}\n\n    def get_valid_target_types(self, column):\n        valid_target_types = column.valid_target_types\n        if valid_target_types:\n            valid_target_type_ids = tuple(\n                db_type.id for db_type in valid_target_types\n            )\n            return valid_target_type_ids\n", "path": "mathesar/api/serializers/columns.py"}], "after_files": [{"content": "from rest_framework import serializers, status\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import empty, SerializerMethodField\nfrom rest_framework.settings import api_settings\n\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\nfrom mathesar.api.serializers.shared_serializers import (\n    DisplayOptionsMappingSerializer,\n    DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY,\n)\nfrom mathesar.api.exceptions.database_exceptions import (\n    exceptions as database_api_exceptions,\n)\nfrom db.columns.exceptions import InvalidTypeError\nfrom mathesar.models.base import Column\nfrom db.types.operations.convert import get_db_type_enum_from_id\n\n\nclass InputValueField(serializers.CharField):\n    \"\"\"\n    Takes in an arbitrary value. Emulates the record creation endpoint,\n    which takes in arbitrary values (un-validated and un-processed request.data).\n    This field replicates that behavior in a serializer.\n    \"\"\"\n\n    def to_internal_value(self, data):\n        return data\n\n    def to_representation(self, value):\n        return value\n\n\nclass TypeOptionSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    length = serializers.IntegerField(required=False)\n    precision = serializers.IntegerField(required=False)\n    scale = serializers.IntegerField(required=False)\n    fields = serializers.CharField(required=False)\n\n    def validate(self, attrs):\n        if attrs.get('scale', None) is not None and attrs.get('precision', None) is None:\n            attrs['precision'] = 1000\n        return super().validate(attrs)\n\n    def run_validation(self, data=empty):\n        # Ensure that there are no unknown type options passed in.\n        if data is not empty and data is not None:\n            unknown = set(data) - set(self.fields)\n            if unknown:\n                errors = ['Unknown field: {}'.format(field) for field in unknown]\n                raise serializers.ValidationError({\n                    api_settings.NON_FIELD_ERRORS_KEY: errors,\n                })\n\n        return super(TypeOptionSerializer, self).run_validation(data)\n\n\nTYPE_KEY = 'type'\nDISPLAY_OPTIONS_KEY = 'display_options'\n\n\nclass SimpleColumnSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = Column\n        fields = ('id',\n                  'name',\n                  TYPE_KEY,\n                  'type_options',\n                  DISPLAY_OPTIONS_KEY,\n                  )\n    id = serializers.IntegerField(required=False)\n    name = serializers.CharField()\n    # TODO consider renaming type and type_options to db_type and db_type_options\n    # The name of below attribute should match value of TYPE_KEY\n    type = serializers.CharField()\n    type_options = TypeOptionSerializer(required=False, allow_null=True)\n    # The name of below attribute should match value of DISPLAY_OPTIONS_KEY\n    display_options = DisplayOptionsMappingSerializer(required=False, allow_null=True)\n\n    def to_representation(self, instance):\n        if isinstance(instance, dict):\n            db_type_id = instance.get(TYPE_KEY)\n            db_type = get_db_type_enum_from_id(db_type_id)\n        else:\n            db_type = instance.db_type\n        # TODO replace or remove this assert before production\n        assert db_type is not None\n        self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type\n        representation = super().to_representation(instance)\n        _force_canonical_type(representation, db_type)\n        return representation\n\n    def to_internal_value(self, data):\n        if self.partial and TYPE_KEY not in data:\n            db_type = getattr(self.instance, 'db_type', None)\n        else:\n            db_type_id = data.get(TYPE_KEY, None)\n            db_type = get_db_type_enum_from_id(db_type_id) if db_type_id else None\n        self.context[DISPLAY_OPTIONS_SERIALIZER_MAPPING_KEY] = db_type\n        return super().to_internal_value(data)\n\n\ndef _force_canonical_type(representation, db_type):\n    \"\"\"\n    Sometimes the representation's TYPE_KEY attribute will also include type option information\n    (e.g. `numeric(3, 5)`). We override the attribute's value to a canonical type id.\n\n    This might be better solved upstream, but since our Column model subclasses SA's Column,\n    overriding its TYPE_KEY attribute, might interfere with SA's workings.\n    \"\"\"\n    representation[TYPE_KEY] = db_type.id\n    return representation\n\n\nclass ColumnDefaultSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    value = InputValueField()\n    is_dynamic = serializers.BooleanField(read_only=True)\n\n\nclass ColumnSerializer(SimpleColumnSerializer):\n    class Meta(SimpleColumnSerializer.Meta):\n        fields = SimpleColumnSerializer.Meta.fields + (\n            'nullable',\n            'primary_key',\n            'source_column',\n            'copy_source_data',\n            'copy_source_constraints',\n            'valid_target_types',\n            'default',\n            'has_dependents',\n        )\n        model_fields = (DISPLAY_OPTIONS_KEY,)\n\n    name = serializers.CharField(required=False, allow_blank=True)\n\n    # From scratch fields\n    type = serializers.CharField(required=False)\n    nullable = serializers.BooleanField(default=True)\n    primary_key = serializers.BooleanField(default=False)\n    default = ColumnDefaultSerializer(\n        source='column_default_dict', required=False, allow_null=True, default=None\n    )\n\n    # From duplication fields\n    source_column = serializers.PrimaryKeyRelatedField(queryset=Column.current_objects.all(), required=False, write_only=True)\n    copy_source_data = serializers.BooleanField(default=True, write_only=True)\n    copy_source_constraints = serializers.BooleanField(default=True, write_only=True)\n\n    # Read only fields\n    valid_target_types = SerializerMethodField(method_name='get_valid_target_types', read_only=True)\n\n    def validate(self, data):\n        data = super().validate(data)\n        # Reevaluate column display options based on the new column type.\n        if TYPE_KEY in data and self.instance:\n            db_type = get_db_type_enum_from_id(data[TYPE_KEY].lower())\n            target_types = self.instance.valid_target_types\n            if db_type not in target_types:\n                raise database_api_exceptions.InvalidTypeCastAPIException(\n                    InvalidTypeError,\n                    status_code=status.HTTP_400_BAD_REQUEST\n                )\n            if DISPLAY_OPTIONS_KEY not in data:\n                db_type = getattr(self.instance, 'db_type', None)\n                # Invalidate display_options if type has been changed\n                if db_type is not None:\n                    if str(db_type.id) != data[TYPE_KEY]:\n                        data[DISPLAY_OPTIONS_KEY] = None\n        if not self.partial:\n            from_scratch_required_fields = [TYPE_KEY]\n            from_scratch_specific_fields = [TYPE_KEY, 'nullable', 'primary_key']\n            from_dupe_required_fields = ['source_column']\n            from_dupe_specific_fields = ['source_column', 'copy_source_data',\n                                         'copy_source_constraints']\n\n            # Note that we run validation on self.initial_data, as `data` has defaults\n            # filled in for fields that weren't specified by the request\n            from_scratch_required_all = all([\n                f in self.initial_data for f in from_scratch_required_fields\n            ])\n            from_scratch_specific_in = [\n                f for f in from_scratch_specific_fields if f in self.initial_data\n            ]\n            from_dupe_required_all = all([\n                f in self.initial_data for f in from_dupe_required_fields\n            ])\n            from_dupe_specific_in = [\n                f for f in from_dupe_specific_fields if f in self.initial_data\n            ]\n\n            if len(from_dupe_specific_in) and len(from_scratch_specific_in):\n                raise ValidationError(\n                    f'{from_scratch_specific_in} cannot be passed in if '\n                    f'{from_dupe_specific_in} has also been passed in.'\n                )\n            elif not from_dupe_required_all and not from_scratch_required_all:\n                # We default to from scratch required fields if no fields are passed\n                if len(from_dupe_specific_in) and not len(from_scratch_specific_in):\n                    required_fields = from_dupe_required_fields\n                else:\n                    required_fields = from_scratch_required_fields\n                raise ValidationError({\n                    f: ['This field is required.']\n                    for f in required_fields\n                    if f not in self.initial_data\n                })\n        return data\n\n    @property\n    def validated_model_fields(self):\n        return {key: self.validated_data[key] for key in self.validated_data if key in self.Meta.model_fields}\n\n    def get_valid_target_types(self, column):\n        valid_target_types = column.valid_target_types\n        if valid_target_types:\n            valid_target_type_ids = tuple(\n                db_type.id for db_type in valid_target_types\n            )\n            return valid_target_type_ids\n", "path": "mathesar/api/serializers/columns.py"}]}