We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Don't allow public schema to be edited
We currently allow editing the public schema's name and description from the schema page. We should disable this.


<img width="968" alt="Screen Shot 2022-11-18 at 12 03 58 PM" src="https://user-images.githubusercontent.com/287034/202761131-265da99a-6f76-4f02-b680-7a98851427b3.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/exceptions/validation_exceptions/exceptions.py`
Content:
```
1 from mathesar.api.exceptions.error_codes import ErrorCodes
2 from mathesar.api.exceptions.validation_exceptions.base_exceptions import MathesarValidationException
3 
4 
5 class DistinctColumnRequiredAPIException(MathesarValidationException):
6     error_code = ErrorCodes.DistinctColumnNameRequired.value
7 
8     def __init__(
9             self,
10             message="Column names must be distinct",
11             field=None,
12             details=None,
13     ):
14         super().__init__(None, self.error_code, message, field, details)
15 
16 
17 class ColumnSizeMismatchAPIException(MathesarValidationException):
18     error_code = ErrorCodes.ColumnSizeMismatch.value
19 
20     def __init__(
21             self,
22             message="Incorrect number of columns in request.",
23             field=None,
24             details=None,
25     ):
26         super().__init__(None, self.error_code, message, field, details)
27 
28 
29 class InvalidLinkChoiceAPIException(MathesarValidationException):
30     error_code = ErrorCodes.InvalidLinkChoice.value
31 
32     def __init__(
33             self,
34             message="Invalid Link type",
35             field=None,
36             details=None,
37     ):
38         super().__init__(None, self.error_code, message, field, details)
39 
40 
41 class MultipleDataFileAPIException(MathesarValidationException):
42     error_code = ErrorCodes.MultipleDataFiles.value
43 
44     def __init__(
45             self,
46             message="Multiple data files are unsupported.",
47             field=None,
48             details=None,
49     ):
50         super().__init__(None, self.error_code, message, field, details)
51 
52 
53 class UnknownDatabaseTypeIdentifier(MathesarValidationException):
54     error_code = ErrorCodes.UnknownDBType.value
55 
56     def __init__(
57             self,
58             db_type_id,
59             field=None,
60             details=None,
61     ):
62         message = f"Unknown database type identifier {db_type_id}."
63         super().__init__(None, self.error_code, message, field, details)
64 
65 
66 class MoneyDisplayOptionValueConflictAPIException(MathesarValidationException):
67     error_code = ErrorCodes.MoneyDisplayOptionConflict.value
68 
69     def __init__(
70             self,
71             message="Money type cannot specify a currency code display option as well as other display options.",
72             field=None,
73             details=None,
74     ):
75         super().__init__(None, self.error_code, message, field, details)
76 
77 
78 class IncompatibleFractionDigitValuesAPIException(MathesarValidationException):
79     error_code = ErrorCodes.IncompatibleFractionDigitValues.value
80 
81     def __init__(
82             self,
83             message="maximum_fraction_digits cannot be less than minimum_fraction_digits.",
84             field=None,
85             details=None,
86     ):
87         super().__init__(None, self.error_code, message, field, details)
88 
89 
90 class UnsupportedConstraintAPIException(MathesarValidationException):
91     error_code = ErrorCodes.UnsupportedConstraint.value
92 
93     def __init__(
94             self,
95             constraint_type,
96             field=None,
97     ):
98         message = f"Operations related to {constraint_type} constraint are currently not supported"
99         super().__init__(None, self.error_code, message, field, None)
100 
101 
102 class ConstraintColumnEmptyAPIException(MathesarValidationException):
103     error_code = ErrorCodes.ConstraintColumnEmpty.value
104 
105     def __init__(
106             self,
107             field=None,
108     ):
109         message = "Constraint column field cannot be empty"
110         super().__init__(None, self.error_code, message, field, None)
111 
112 
113 class InvalidValueType(MathesarValidationException):
114     error_code = ErrorCodes.InvalidValueType.value
115 
116     def __init__(
117             self,
118             message=None,
119             field=None,
120     ):
121         if message is None:
122             message = "Value's type is invalid."
123         super().__init__(None, self.error_code, message, field, None)
124 
125 
126 class DictHasBadKeys(MathesarValidationException):
127     error_code = ErrorCodes.DictHasBadKeys.value
128 
129     def __init__(
130             self,
131             message=None,
132             field=None,
133     ):
134         if message is None:
135             message = "Dictionary's keys are invalid or obligatory keys are missing."
136         super().__init__(None, self.error_code, message, field, None)
137 
138 
139 class InvalidTableName(MathesarValidationException):
140     error_code = ErrorCodes.InvalidTableName.value
141 
142     def __init__(
143             self,
144             table_name,
145             message=None,
146             field=None,
147     ):
148         message = f'Table name "{table_name}" is invalid.'
149         super().__init__(None, self.error_code, message, field, None)
150 
151 
152 class IncorrectOldPassword(MathesarValidationException):
153     error_code = ErrorCodes.IncorrectOldPassword.value
154 
155     def __init__(
156             self,
157 
158             field=None,
159     ):
160         message = "Old password is not correct"
161         super().__init__(None, self.error_code, message, field, None)
162
```

Path: `mathesar/api/exceptions/error_codes.py`
Content:
```
1 from enum import Enum, unique
2 
3 
4 @unique
5 class ErrorCodes(Enum):
6     # Matches with default code of drf-friendly-errors library
7     # API Error
8     MethodNotAllowed = 4006
9     NotFound = 4005
10     TableNotFound = 4041
11     RecordNotFound = 4042
12     UnknownError = 4999
13     # Generic Errors
14     ProgrammingError = 4101
15     TypeError = 4102
16     ValueError = 4103
17 
18     # DB Error Codes
19     DuplicateTableError = 4205
20     DuplicateColumnError = 4206
21     ExclusionViolation = 4213
22     ForeignKeyViolation = 4212
23     InvalidTypeCast = 4203
24     InvalidTypeOption = 4210
25     InvalidDefault = 4211
26     NonClassifiedIntegrityError = 4201
27     NotNullViolation = 4204
28     RaiseException = 4202
29     TypeMismatchViolation = 4214
30     UndefinedFunction = 4207
31     UniqueViolation = 4208
32     UnsupportedType = 4209
33 
34     # Data Imports error code
35     InvalidTableError = 4301
36     UniqueImportViolation = 4303
37 
38     # Validation Error
39     ColumnSizeMismatch = 4401
40     DistinctColumnNameRequired = 4402
41     MappingsNotFound = 4417
42     MultipleDataFiles = 4400
43     MoneyDisplayOptionConflict = 4407
44     UnsupportedAlter = 4403
45     URLDownloadError = 4404
46     URLNotReachableError = 4405
47     URLInvalidContentType = 4406
48     UnknownDBType = 4408
49     InvalidDateError = 4413
50     InvalidDateFormatError = 4414
51     InvalidLinkChoice = 4409
52     InvalidTableName = 4420
53     IncompatibleFractionDigitValues = 4410
54     UnsupportedConstraint = 4411
55     ConstraintColumnEmpty = 4412
56     InvalidValueType = 4415
57     DictHasBadKeys = 4416
58     DeletedColumnAccess = 4418
59     IncorrectOldPassword = 4419
60
```

Path: `mathesar/api/db/viewsets/schemas.py`
Content:
```
1 from django_filters import rest_framework as filters
2 from rest_access_policy import AccessViewSetMixin
3 from rest_framework import status, viewsets
4 from rest_framework.decorators import action
5 from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
6 from rest_framework.response import Response
7 
8 from mathesar.api.db.permissions.schema import SchemaAccessPolicy
9 from mathesar.api.dj_filters import SchemaFilter
10 from mathesar.api.pagination import DefaultLimitOffsetPagination
11 from mathesar.api.serializers.dependents import DependentSerializer, DependentFilterSerializer
12 from mathesar.api.serializers.schemas import SchemaSerializer
13 from mathesar.models.base import Schema
14 from mathesar.utils.schemas import create_schema_and_object
15 
16 
17 class SchemaViewSet(AccessViewSetMixin, viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):
18     serializer_class = SchemaSerializer
19     pagination_class = DefaultLimitOffsetPagination
20     filter_backends = (filters.DjangoFilterBackend,)
21     filterset_class = SchemaFilter
22     access_policy = SchemaAccessPolicy
23 
24     def get_queryset(self):
25         qs = Schema.objects.all().order_by('-created_at')
26         return self.access_policy.scope_viewset_queryset(self.request, qs)
27 
28     def create(self, request):
29         serializer = SchemaSerializer(data=request.data, context={'request': request})
30         serializer.is_valid(raise_exception=True)
31         database_name = serializer.validated_data['database'].name
32         schema = create_schema_and_object(
33             serializer.validated_data['name'],
34             database_name,
35             comment=serializer.validated_data.get('description')
36         )
37         serializer = SchemaSerializer(schema)
38         return Response(serializer.data, status=status.HTTP_201_CREATED)
39 
40     def partial_update(self, request, pk=None):
41         serializer = SchemaSerializer(
42             data=request.data, context={'request': request}, partial=True
43         )
44         serializer.is_valid(raise_exception=True)
45 
46         schema = self.get_object()
47         schema.update_sa_schema(serializer.validated_data)
48 
49         # Reload the schema to avoid cached properties
50         schema = self.get_object()
51         schema.clear_name_cache()
52         serializer = SchemaSerializer(schema, context={'request': request})
53         return Response(serializer.data)
54 
55     def destroy(self, request, pk=None):
56         schema = self.get_object()
57         schema.delete_sa_schema()
58         return Response(status=status.HTTP_204_NO_CONTENT)
59 
60     @action(methods=['get'], detail=True)
61     def dependents(self, request, pk=None):
62         serializer = DependentFilterSerializer(data=request.GET)
63         serializer.is_valid(raise_exception=True)
64         types_exclude = serializer.validated_data['exclude']
65 
66         schema = self.get_object()
67         serializer = DependentSerializer(schema.get_dependents(types_exclude), many=True, context={'request': request})
68         return Response(serializer.data)
69
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mathesar/api/db/viewsets/schemas.py b/mathesar/api/db/viewsets/schemas.py
--- a/mathesar/api/db/viewsets/schemas.py
+++ b/mathesar/api/db/viewsets/schemas.py
@@ -12,6 +12,7 @@
 from mathesar.api.serializers.schemas import SchemaSerializer
 from mathesar.models.base import Schema
 from mathesar.utils.schemas import create_schema_and_object
+from mathesar.api.exceptions.validation_exceptions.exceptions import EditingPublicSchemaIsDisallowed
 
 
 class SchemaViewSet(AccessViewSetMixin, viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):
@@ -44,6 +45,11 @@
         serializer.is_valid(raise_exception=True)
 
         schema = self.get_object()
+
+        # We forbid editing the public schema
+        if schema.name == "public":
+            raise EditingPublicSchemaIsDisallowed()
+
         schema.update_sa_schema(serializer.validated_data)
 
         # Reload the schema to avoid cached properties
diff --git a/mathesar/api/exceptions/error_codes.py b/mathesar/api/exceptions/error_codes.py
--- a/mathesar/api/exceptions/error_codes.py
+++ b/mathesar/api/exceptions/error_codes.py
@@ -57,3 +57,4 @@
     DictHasBadKeys = 4416
     DeletedColumnAccess = 4418
     IncorrectOldPassword = 4419
+    EditingPublicSchema = 4421
diff --git a/mathesar/api/exceptions/validation_exceptions/exceptions.py b/mathesar/api/exceptions/validation_exceptions/exceptions.py
--- a/mathesar/api/exceptions/validation_exceptions/exceptions.py
+++ b/mathesar/api/exceptions/validation_exceptions/exceptions.py
@@ -154,8 +154,18 @@
 
     def __init__(
             self,
-
             field=None,
     ):
-        message = "Old password is not correct"
+        message = "Old password is not correct."
         super().__init__(None, self.error_code, message, field, None)
+
+
+class EditingPublicSchemaIsDisallowed(MathesarValidationException):
+    error_code = ErrorCodes.EditingPublicSchema.value
+
+    def __init__(
+            self,
+            message="Editing the public schema is disallowed.",
+            field=None,
+    ):
+        super().__init__(None, self.error_code, message, field)

{"golden_diff": "diff --git a/mathesar/api/db/viewsets/schemas.py b/mathesar/api/db/viewsets/schemas.py\n--- a/mathesar/api/db/viewsets/schemas.py\n+++ b/mathesar/api/db/viewsets/schemas.py\n@@ -12,6 +12,7 @@\n from mathesar.api.serializers.schemas import SchemaSerializer\n from mathesar.models.base import Schema\n from mathesar.utils.schemas import create_schema_and_object\n+from mathesar.api.exceptions.validation_exceptions.exceptions import EditingPublicSchemaIsDisallowed\n \n \n class SchemaViewSet(AccessViewSetMixin, viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):\n@@ -44,6 +45,11 @@\n         serializer.is_valid(raise_exception=True)\n \n         schema = self.get_object()\n+\n+        # We forbid editing the public schema\n+        if schema.name == \"public\":\n+            raise EditingPublicSchemaIsDisallowed()\n+\n         schema.update_sa_schema(serializer.validated_data)\n \n         # Reload the schema to avoid cached properties\ndiff --git a/mathesar/api/exceptions/error_codes.py b/mathesar/api/exceptions/error_codes.py\n--- a/mathesar/api/exceptions/error_codes.py\n+++ b/mathesar/api/exceptions/error_codes.py\n@@ -57,3 +57,4 @@\n     DictHasBadKeys = 4416\n     DeletedColumnAccess = 4418\n     IncorrectOldPassword = 4419\n+    EditingPublicSchema = 4421\ndiff --git a/mathesar/api/exceptions/validation_exceptions/exceptions.py b/mathesar/api/exceptions/validation_exceptions/exceptions.py\n--- a/mathesar/api/exceptions/validation_exceptions/exceptions.py\n+++ b/mathesar/api/exceptions/validation_exceptions/exceptions.py\n@@ -154,8 +154,18 @@\n \n     def __init__(\n             self,\n-\n             field=None,\n     ):\n-        message = \"Old password is not correct\"\n+        message = \"Old password is not correct.\"\n         super().__init__(None, self.error_code, message, field, None)\n+\n+\n+class EditingPublicSchemaIsDisallowed(MathesarValidationException):\n+    error_code = ErrorCodes.EditingPublicSchema.value\n+\n+    def __init__(\n+            self,\n+            message=\"Editing the public schema is disallowed.\",\n+            field=None,\n+    ):\n+        super().__init__(None, self.error_code, message, field)\n", "issue": "Don't allow public schema to be edited\nWe currently allow editing the public schema's name and description from the schema page. We should disable this.\r\n\r\n\r\n<img width=\"968\" alt=\"Screen Shot 2022-11-18 at 12 03 58 PM\" src=\"https://user-images.githubusercontent.com/287034/202761131-265da99a-6f76-4f02-b680-7a98851427b3.png\">\r\n\n", "before_files": [{"content": "from mathesar.api.exceptions.error_codes import ErrorCodes\nfrom mathesar.api.exceptions.validation_exceptions.base_exceptions import MathesarValidationException\n\n\nclass DistinctColumnRequiredAPIException(MathesarValidationException):\n    error_code = ErrorCodes.DistinctColumnNameRequired.value\n\n    def __init__(\n            self,\n            message=\"Column names must be distinct\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass ColumnSizeMismatchAPIException(MathesarValidationException):\n    error_code = ErrorCodes.ColumnSizeMismatch.value\n\n    def __init__(\n            self,\n            message=\"Incorrect number of columns in request.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass InvalidLinkChoiceAPIException(MathesarValidationException):\n    error_code = ErrorCodes.InvalidLinkChoice.value\n\n    def __init__(\n            self,\n            message=\"Invalid Link type\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass MultipleDataFileAPIException(MathesarValidationException):\n    error_code = ErrorCodes.MultipleDataFiles.value\n\n    def __init__(\n            self,\n            message=\"Multiple data files are unsupported.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass UnknownDatabaseTypeIdentifier(MathesarValidationException):\n    error_code = ErrorCodes.UnknownDBType.value\n\n    def __init__(\n            self,\n            db_type_id,\n            field=None,\n            details=None,\n    ):\n        message = f\"Unknown database type identifier {db_type_id}.\"\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass MoneyDisplayOptionValueConflictAPIException(MathesarValidationException):\n    error_code = ErrorCodes.MoneyDisplayOptionConflict.value\n\n    def __init__(\n            self,\n            message=\"Money type cannot specify a currency code display option as well as other display options.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass IncompatibleFractionDigitValuesAPIException(MathesarValidationException):\n    error_code = ErrorCodes.IncompatibleFractionDigitValues.value\n\n    def __init__(\n            self,\n            message=\"maximum_fraction_digits cannot be less than minimum_fraction_digits.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass UnsupportedConstraintAPIException(MathesarValidationException):\n    error_code = ErrorCodes.UnsupportedConstraint.value\n\n    def __init__(\n            self,\n            constraint_type,\n            field=None,\n    ):\n        message = f\"Operations related to {constraint_type} constraint are currently not supported\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass ConstraintColumnEmptyAPIException(MathesarValidationException):\n    error_code = ErrorCodes.ConstraintColumnEmpty.value\n\n    def __init__(\n            self,\n            field=None,\n    ):\n        message = \"Constraint column field cannot be empty\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass InvalidValueType(MathesarValidationException):\n    error_code = ErrorCodes.InvalidValueType.value\n\n    def __init__(\n            self,\n            message=None,\n            field=None,\n    ):\n        if message is None:\n            message = \"Value's type is invalid.\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass DictHasBadKeys(MathesarValidationException):\n    error_code = ErrorCodes.DictHasBadKeys.value\n\n    def __init__(\n            self,\n            message=None,\n            field=None,\n    ):\n        if message is None:\n            message = \"Dictionary's keys are invalid or obligatory keys are missing.\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass InvalidTableName(MathesarValidationException):\n    error_code = ErrorCodes.InvalidTableName.value\n\n    def __init__(\n            self,\n            table_name,\n            message=None,\n            field=None,\n    ):\n        message = f'Table name \"{table_name}\" is invalid.'\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass IncorrectOldPassword(MathesarValidationException):\n    error_code = ErrorCodes.IncorrectOldPassword.value\n\n    def __init__(\n            self,\n\n            field=None,\n    ):\n        message = \"Old password is not correct\"\n        super().__init__(None, self.error_code, message, field, None)\n", "path": "mathesar/api/exceptions/validation_exceptions/exceptions.py"}, {"content": "from enum import Enum, unique\n\n\n@unique\nclass ErrorCodes(Enum):\n    # Matches with default code of drf-friendly-errors library\n    # API Error\n    MethodNotAllowed = 4006\n    NotFound = 4005\n    TableNotFound = 4041\n    RecordNotFound = 4042\n    UnknownError = 4999\n    # Generic Errors\n    ProgrammingError = 4101\n    TypeError = 4102\n    ValueError = 4103\n\n    # DB Error Codes\n    DuplicateTableError = 4205\n    DuplicateColumnError = 4206\n    ExclusionViolation = 4213\n    ForeignKeyViolation = 4212\n    InvalidTypeCast = 4203\n    InvalidTypeOption = 4210\n    InvalidDefault = 4211\n    NonClassifiedIntegrityError = 4201\n    NotNullViolation = 4204\n    RaiseException = 4202\n    TypeMismatchViolation = 4214\n    UndefinedFunction = 4207\n    UniqueViolation = 4208\n    UnsupportedType = 4209\n\n    # Data Imports error code\n    InvalidTableError = 4301\n    UniqueImportViolation = 4303\n\n    # Validation Error\n    ColumnSizeMismatch = 4401\n    DistinctColumnNameRequired = 4402\n    MappingsNotFound = 4417\n    MultipleDataFiles = 4400\n    MoneyDisplayOptionConflict = 4407\n    UnsupportedAlter = 4403\n    URLDownloadError = 4404\n    URLNotReachableError = 4405\n    URLInvalidContentType = 4406\n    UnknownDBType = 4408\n    InvalidDateError = 4413\n    InvalidDateFormatError = 4414\n    InvalidLinkChoice = 4409\n    InvalidTableName = 4420\n    IncompatibleFractionDigitValues = 4410\n    UnsupportedConstraint = 4411\n    ConstraintColumnEmpty = 4412\n    InvalidValueType = 4415\n    DictHasBadKeys = 4416\n    DeletedColumnAccess = 4418\n    IncorrectOldPassword = 4419\n", "path": "mathesar/api/exceptions/error_codes.py"}, {"content": "from django_filters import rest_framework as filters\nfrom rest_access_policy import AccessViewSetMixin\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.mixins import ListModelMixin, RetrieveModelMixin\nfrom rest_framework.response import Response\n\nfrom mathesar.api.db.permissions.schema import SchemaAccessPolicy\nfrom mathesar.api.dj_filters import SchemaFilter\nfrom mathesar.api.pagination import DefaultLimitOffsetPagination\nfrom mathesar.api.serializers.dependents import DependentSerializer, DependentFilterSerializer\nfrom mathesar.api.serializers.schemas import SchemaSerializer\nfrom mathesar.models.base import Schema\nfrom mathesar.utils.schemas import create_schema_and_object\n\n\nclass SchemaViewSet(AccessViewSetMixin, viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):\n    serializer_class = SchemaSerializer\n    pagination_class = DefaultLimitOffsetPagination\n    filter_backends = (filters.DjangoFilterBackend,)\n    filterset_class = SchemaFilter\n    access_policy = SchemaAccessPolicy\n\n    def get_queryset(self):\n        qs = Schema.objects.all().order_by('-created_at')\n        return self.access_policy.scope_viewset_queryset(self.request, qs)\n\n    def create(self, request):\n        serializer = SchemaSerializer(data=request.data, context={'request': request})\n        serializer.is_valid(raise_exception=True)\n        database_name = serializer.validated_data['database'].name\n        schema = create_schema_and_object(\n            serializer.validated_data['name'],\n            database_name,\n            comment=serializer.validated_data.get('description')\n        )\n        serializer = SchemaSerializer(schema)\n        return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n    def partial_update(self, request, pk=None):\n        serializer = SchemaSerializer(\n            data=request.data, context={'request': request}, partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n\n        schema = self.get_object()\n        schema.update_sa_schema(serializer.validated_data)\n\n        # Reload the schema to avoid cached properties\n        schema = self.get_object()\n        schema.clear_name_cache()\n        serializer = SchemaSerializer(schema, context={'request': request})\n        return Response(serializer.data)\n\n    def destroy(self, request, pk=None):\n        schema = self.get_object()\n        schema.delete_sa_schema()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    @action(methods=['get'], detail=True)\n    def dependents(self, request, pk=None):\n        serializer = DependentFilterSerializer(data=request.GET)\n        serializer.is_valid(raise_exception=True)\n        types_exclude = serializer.validated_data['exclude']\n\n        schema = self.get_object()\n        serializer = DependentSerializer(schema.get_dependents(types_exclude), many=True, context={'request': request})\n        return Response(serializer.data)\n", "path": "mathesar/api/db/viewsets/schemas.py"}], "after_files": [{"content": "from mathesar.api.exceptions.error_codes import ErrorCodes\nfrom mathesar.api.exceptions.validation_exceptions.base_exceptions import MathesarValidationException\n\n\nclass DistinctColumnRequiredAPIException(MathesarValidationException):\n    error_code = ErrorCodes.DistinctColumnNameRequired.value\n\n    def __init__(\n            self,\n            message=\"Column names must be distinct\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass ColumnSizeMismatchAPIException(MathesarValidationException):\n    error_code = ErrorCodes.ColumnSizeMismatch.value\n\n    def __init__(\n            self,\n            message=\"Incorrect number of columns in request.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass InvalidLinkChoiceAPIException(MathesarValidationException):\n    error_code = ErrorCodes.InvalidLinkChoice.value\n\n    def __init__(\n            self,\n            message=\"Invalid Link type\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass MultipleDataFileAPIException(MathesarValidationException):\n    error_code = ErrorCodes.MultipleDataFiles.value\n\n    def __init__(\n            self,\n            message=\"Multiple data files are unsupported.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass UnknownDatabaseTypeIdentifier(MathesarValidationException):\n    error_code = ErrorCodes.UnknownDBType.value\n\n    def __init__(\n            self,\n            db_type_id,\n            field=None,\n            details=None,\n    ):\n        message = f\"Unknown database type identifier {db_type_id}.\"\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass MoneyDisplayOptionValueConflictAPIException(MathesarValidationException):\n    error_code = ErrorCodes.MoneyDisplayOptionConflict.value\n\n    def __init__(\n            self,\n            message=\"Money type cannot specify a currency code display option as well as other display options.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass IncompatibleFractionDigitValuesAPIException(MathesarValidationException):\n    error_code = ErrorCodes.IncompatibleFractionDigitValues.value\n\n    def __init__(\n            self,\n            message=\"maximum_fraction_digits cannot be less than minimum_fraction_digits.\",\n            field=None,\n            details=None,\n    ):\n        super().__init__(None, self.error_code, message, field, details)\n\n\nclass UnsupportedConstraintAPIException(MathesarValidationException):\n    error_code = ErrorCodes.UnsupportedConstraint.value\n\n    def __init__(\n            self,\n            constraint_type,\n            field=None,\n    ):\n        message = f\"Operations related to {constraint_type} constraint are currently not supported\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass ConstraintColumnEmptyAPIException(MathesarValidationException):\n    error_code = ErrorCodes.ConstraintColumnEmpty.value\n\n    def __init__(\n            self,\n            field=None,\n    ):\n        message = \"Constraint column field cannot be empty\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass InvalidValueType(MathesarValidationException):\n    error_code = ErrorCodes.InvalidValueType.value\n\n    def __init__(\n            self,\n            message=None,\n            field=None,\n    ):\n        if message is None:\n            message = \"Value's type is invalid.\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass DictHasBadKeys(MathesarValidationException):\n    error_code = ErrorCodes.DictHasBadKeys.value\n\n    def __init__(\n            self,\n            message=None,\n            field=None,\n    ):\n        if message is None:\n            message = \"Dictionary's keys are invalid or obligatory keys are missing.\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass InvalidTableName(MathesarValidationException):\n    error_code = ErrorCodes.InvalidTableName.value\n\n    def __init__(\n            self,\n            table_name,\n            message=None,\n            field=None,\n    ):\n        message = f'Table name \"{table_name}\" is invalid.'\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass IncorrectOldPassword(MathesarValidationException):\n    error_code = ErrorCodes.IncorrectOldPassword.value\n\n    def __init__(\n            self,\n            field=None,\n    ):\n        message = \"Old password is not correct.\"\n        super().__init__(None, self.error_code, message, field, None)\n\n\nclass EditingPublicSchemaIsDisallowed(MathesarValidationException):\n    error_code = ErrorCodes.EditingPublicSchema.value\n\n    def __init__(\n            self,\n            message=\"Editing the public schema is disallowed.\",\n            field=None,\n    ):\n        super().__init__(None, self.error_code, message, field)\n", "path": "mathesar/api/exceptions/validation_exceptions/exceptions.py"}, {"content": "from enum import Enum, unique\n\n\n@unique\nclass ErrorCodes(Enum):\n    # Matches with default code of drf-friendly-errors library\n    # API Error\n    MethodNotAllowed = 4006\n    NotFound = 4005\n    TableNotFound = 4041\n    RecordNotFound = 4042\n    UnknownError = 4999\n    # Generic Errors\n    ProgrammingError = 4101\n    TypeError = 4102\n    ValueError = 4103\n\n    # DB Error Codes\n    DuplicateTableError = 4205\n    DuplicateColumnError = 4206\n    ExclusionViolation = 4213\n    ForeignKeyViolation = 4212\n    InvalidTypeCast = 4203\n    InvalidTypeOption = 4210\n    InvalidDefault = 4211\n    NonClassifiedIntegrityError = 4201\n    NotNullViolation = 4204\n    RaiseException = 4202\n    TypeMismatchViolation = 4214\n    UndefinedFunction = 4207\n    UniqueViolation = 4208\n    UnsupportedType = 4209\n\n    # Data Imports error code\n    InvalidTableError = 4301\n    UniqueImportViolation = 4303\n\n    # Validation Error\n    ColumnSizeMismatch = 4401\n    DistinctColumnNameRequired = 4402\n    MappingsNotFound = 4417\n    MultipleDataFiles = 4400\n    MoneyDisplayOptionConflict = 4407\n    UnsupportedAlter = 4403\n    URLDownloadError = 4404\n    URLNotReachableError = 4405\n    URLInvalidContentType = 4406\n    UnknownDBType = 4408\n    InvalidDateError = 4413\n    InvalidDateFormatError = 4414\n    InvalidLinkChoice = 4409\n    InvalidTableName = 4420\n    IncompatibleFractionDigitValues = 4410\n    UnsupportedConstraint = 4411\n    ConstraintColumnEmpty = 4412\n    InvalidValueType = 4415\n    DictHasBadKeys = 4416\n    DeletedColumnAccess = 4418\n    IncorrectOldPassword = 4419\n    EditingPublicSchema = 4421\n", "path": "mathesar/api/exceptions/error_codes.py"}, {"content": "from django_filters import rest_framework as filters\nfrom rest_access_policy import AccessViewSetMixin\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.mixins import ListModelMixin, RetrieveModelMixin\nfrom rest_framework.response import Response\n\nfrom mathesar.api.db.permissions.schema import SchemaAccessPolicy\nfrom mathesar.api.dj_filters import SchemaFilter\nfrom mathesar.api.pagination import DefaultLimitOffsetPagination\nfrom mathesar.api.serializers.dependents import DependentSerializer, DependentFilterSerializer\nfrom mathesar.api.serializers.schemas import SchemaSerializer\nfrom mathesar.models.base import Schema\nfrom mathesar.utils.schemas import create_schema_and_object\nfrom mathesar.api.exceptions.validation_exceptions.exceptions import EditingPublicSchemaIsDisallowed\n\n\nclass SchemaViewSet(AccessViewSetMixin, viewsets.GenericViewSet, ListModelMixin, RetrieveModelMixin):\n    serializer_class = SchemaSerializer\n    pagination_class = DefaultLimitOffsetPagination\n    filter_backends = (filters.DjangoFilterBackend,)\n    filterset_class = SchemaFilter\n    access_policy = SchemaAccessPolicy\n\n    def get_queryset(self):\n        qs = Schema.objects.all().order_by('-created_at')\n        return self.access_policy.scope_viewset_queryset(self.request, qs)\n\n    def create(self, request):\n        serializer = SchemaSerializer(data=request.data, context={'request': request})\n        serializer.is_valid(raise_exception=True)\n        database_name = serializer.validated_data['database'].name\n        schema = create_schema_and_object(\n            serializer.validated_data['name'],\n            database_name,\n            comment=serializer.validated_data.get('description')\n        )\n        serializer = SchemaSerializer(schema)\n        return Response(serializer.data, status=status.HTTP_201_CREATED)\n\n    def partial_update(self, request, pk=None):\n        serializer = SchemaSerializer(\n            data=request.data, context={'request': request}, partial=True\n        )\n        serializer.is_valid(raise_exception=True)\n\n        schema = self.get_object()\n\n        # We forbid editing the public schema\n        if schema.name == \"public\":\n            raise EditingPublicSchemaIsDisallowed()\n\n        schema.update_sa_schema(serializer.validated_data)\n\n        # Reload the schema to avoid cached properties\n        schema = self.get_object()\n        schema.clear_name_cache()\n        serializer = SchemaSerializer(schema, context={'request': request})\n        return Response(serializer.data)\n\n    def destroy(self, request, pk=None):\n        schema = self.get_object()\n        schema.delete_sa_schema()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n\n    @action(methods=['get'], detail=True)\n    def dependents(self, request, pk=None):\n        serializer = DependentFilterSerializer(data=request.GET)\n        serializer.is_valid(raise_exception=True)\n        types_exclude = serializer.validated_data['exclude']\n\n        schema = self.get_object()\n        serializer = DependentSerializer(schema.get_dependents(types_exclude), many=True, context={'request': request})\n        return Response(serializer.data)\n", "path": "mathesar/api/db/viewsets/schemas.py"}]}