We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Money formatting should start off with correct decimal places
By default, money columns should have 2 decimal places in the display options.

Currently, when you import the money in the demo video, there are values like `$15.6` – by default, that should be `$15.60`.

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

{"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@@ -5,7 +5,7 @@\n \n from db.columns.exceptions import InvalidTypeError\n from db.columns.exceptions import InvalidTypeOptionError\n-from db.types.base import PostgresType\n+from db.types.base import PostgresType, MathesarCustomType\n from db.types.operations.convert import get_db_type_enum_from_id\n from mathesar.api.exceptions.database_exceptions import (\n     exceptions as database_api_exceptions\n@@ -217,6 +217,21 @@\n                 })\n         return data\n \n+    def to_representation(self, instance):\n+        # Set default display_options for mathesar_money type if none are provided.\n+        if (\n+            instance.db_type == MathesarCustomType.MATHESAR_MONEY\n+            and instance.display_options is None\n+        ):\n+            instance.display_options = {\n+                'use_grouping': 'true',\n+                'number_format': None,\n+                'currency_symbol': None,\n+                'maximum_fraction_digits': 2,\n+                'minimum_fraction_digits': 2,\n+                'currency_symbol_location': 'after-minus'}\n+        return super().to_representation(instance)\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", "issue": "Money formatting should start off with correct decimal places\nBy default, money columns should have 2 decimal places in the display options.\r\n\r\nCurrently, when you import the money in the demo video, there are values like `$15.6` \u2013 by default, that should be `$15.60`.\n", "before_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 db.columns.exceptions import InvalidTypeError\nfrom db.columns.exceptions import InvalidTypeOptionError\nfrom db.types.base import PostgresType\nfrom db.types.operations.convert import get_db_type_enum_from_id\nfrom mathesar.api.exceptions.database_exceptions import (\n    exceptions as database_api_exceptions\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\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        db_type = self.context.get('db_type', None)\n        scale = attrs.get('scale', None)\n        precision = attrs.get('precision', None)\n        if (\n            db_type == PostgresType.NUMERIC\n            and (scale is None) != (precision is None)\n        ):\n            raise database_api_exceptions.InvalidTypeOptionAPIException(\n                InvalidTypeOptionError,\n                message='Both scale and precision fields are required.',\n                status_code=status.HTTP_400_BAD_REQUEST,\n            )\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"}], "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 db.columns.exceptions import InvalidTypeError\nfrom db.columns.exceptions import InvalidTypeOptionError\nfrom db.types.base import PostgresType, MathesarCustomType\nfrom db.types.operations.convert import get_db_type_enum_from_id\nfrom mathesar.api.exceptions.database_exceptions import (\n    exceptions as database_api_exceptions\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\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        db_type = self.context.get('db_type', None)\n        scale = attrs.get('scale', None)\n        precision = attrs.get('precision', None)\n        if (\n            db_type == PostgresType.NUMERIC\n            and (scale is None) != (precision is None)\n        ):\n            raise database_api_exceptions.InvalidTypeOptionAPIException(\n                InvalidTypeOptionError,\n                message='Both scale and precision fields are required.',\n                status_code=status.HTTP_400_BAD_REQUEST,\n            )\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    def to_representation(self, instance):\n        # Set default display_options for mathesar_money type if none are provided.\n        if (\n            instance.db_type == MathesarCustomType.MATHESAR_MONEY\n            and instance.display_options is None\n        ):\n            instance.display_options = {\n                'use_grouping': 'true',\n                'number_format': None,\n                'currency_symbol': None,\n                'maximum_fraction_digits': 2,\n                'minimum_fraction_digits': 2,\n                'currency_symbol_location': 'after-minus'}\n        return super().to_representation(instance)\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"}]}