We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement filtering options for Boolean types
## Problem
We need to ensure that records that include columns of Boolean type support the following filters via API: 
- is {true/false}
- is not {true/false}
- is empty
- is not empty

This may already be done, please verify that it works and add some test cases specifically for boolean data types.

This involves:
- Implementing the filters in the backend
- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type
  - Filter information should include the number of parameters needing to be passed in (e.g. `is` needs 1 parameter, `is empty` needs 0)

## Additional context
- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.
- See also: #387
- #557 provides some context on storing filters.

--- 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/serializers.py`
Content:
```
1 import requests
2 
3 from django.urls import reverse
4 from rest_framework import serializers
5 from rest_framework.exceptions import ValidationError
6 from rest_framework.fields import empty
7 from rest_framework.settings import api_settings
8 
9 from mathesar.models import Table, Schema, DataFile, Database, Constraint
10 
11 
12 SUPPORTED_URL_CONTENT_TYPES = {'text/csv', 'text/plain'}
13 
14 
15 class ModelNameField(serializers.CharField):
16     """
17     De-serializes the request field as a string, but serializes the response field as
18     `model.name`. Required to support passing and returing a model name from the
19     endpoint, while also storing the model as a related field.
20     """
21     def to_representation(self, value):
22         return value.name
23 
24 
25 class InputValueField(serializers.CharField):
26     """
27     Takes in an arbitrary value. Use to emulate our column and record creation and
28     update endpoints, which handle arbitrary data pulled from request.data
29     """
30     def to_internal_value(self, data):
31         return data
32 
33     def to_representation(self, value):
34         return value
35 
36 
37 class NestedTableSerializer(serializers.HyperlinkedModelSerializer):
38     url = serializers.SerializerMethodField()
39 
40     class Meta:
41         model = Table
42         fields = ['id', 'name', 'url']
43 
44     def get_url(self, obj):
45         request = self.context['request']
46         return request.build_absolute_uri(reverse('table-detail', kwargs={'pk': obj.pk}))
47 
48 
49 class SchemaSerializer(serializers.HyperlinkedModelSerializer):
50     name = serializers.CharField()
51     database = ModelNameField(max_length=128)
52 
53     class Meta:
54         model = Schema
55         fields = ['id', 'name', 'database', 'has_dependencies']
56 
57 
58 class TypeOptionSerializer(serializers.Serializer):
59     precision = serializers.IntegerField(required=False)
60     scale = serializers.IntegerField(required=False)
61 
62     def run_validation(self, data=empty):
63         # Ensure that there are no unknown type options passed in.
64         if data is not empty:
65             unknown = set(data) - set(self.fields)
66             if unknown:
67                 errors = ['Unknown field: {}'.format(field) for field in unknown]
68                 raise serializers.ValidationError({
69                     api_settings.NON_FIELD_ERRORS_KEY: errors,
70                 })
71 
72         return super(TypeOptionSerializer, self).run_validation(data)
73 
74 
75 class SimpleColumnSerializer(serializers.Serializer):
76     name = serializers.CharField()
77     type = serializers.CharField(source='plain_type')
78     type_options = TypeOptionSerializer(required=False, allow_null=True)
79 
80 
81 class ColumnSerializer(SimpleColumnSerializer):
82     name = serializers.CharField(required=False)
83 
84     # From scratch fields
85     type = serializers.CharField(source='plain_type', required=False)
86     nullable = serializers.BooleanField(default=True)
87     primary_key = serializers.BooleanField(default=False)
88 
89     # From duplication fields
90     source_column = serializers.IntegerField(required=False, write_only=True)
91     copy_source_data = serializers.BooleanField(default=True, write_only=True)
92     copy_source_constraints = serializers.BooleanField(default=True, write_only=True)
93 
94     # Read only fields
95     index = serializers.IntegerField(source='column_index', read_only=True)
96     valid_target_types = serializers.ListField(read_only=True)
97     default = InputValueField(
98         source='default_value', read_only=False, default=None, allow_null=True
99     )
100 
101     def validate(self, data):
102         if not self.partial:
103             from_scratch_required_fields = ['name', 'type']
104             from_scratch_specific_fields = ['type', 'nullable', 'primary_key']
105             from_dupe_required_fields = ['source_column']
106             from_dupe_specific_fields = ['source_column', 'copy_source_data',
107                                          'copy_source_constraints']
108 
109             # Note that we run validation on self.initial_data, as `data` has defaults
110             # filled in for fields that weren't specified by the request
111             from_scratch_required_all = all([
112                 f in self.initial_data for f in from_scratch_required_fields
113             ])
114             from_scratch_specific_in = [
115                 f for f in from_scratch_specific_fields if f in self.initial_data
116             ]
117             from_dupe_required_all = all([
118                 f in self.initial_data for f in from_dupe_required_fields
119             ])
120             from_dupe_specific_in = [
121                 f for f in from_dupe_specific_fields if f in self.initial_data
122             ]
123 
124             if len(from_dupe_specific_in) and len(from_scratch_specific_in):
125                 raise ValidationError(
126                     f'{from_scratch_specific_in} cannot be passed in if '
127                     f'{from_dupe_specific_in} has also been passed in.'
128                 )
129             elif not from_dupe_required_all and not from_scratch_required_all:
130                 # We default to from scratch required fields if no fields are passed
131                 if len(from_dupe_specific_in) and not len(from_scratch_specific_in):
132                     required_fields = from_dupe_required_fields
133                 else:
134                     required_fields = from_scratch_required_fields
135                 raise ValidationError({
136                     f: ['This field is required.']
137                     for f in required_fields
138                     if f not in self.initial_data
139                 })
140         return data
141 
142 
143 class TableSerializer(serializers.ModelSerializer):
144     columns = SimpleColumnSerializer(many=True, source='sa_columns', required=False)
145     records_url = serializers.SerializerMethodField()
146     constraints_url = serializers.SerializerMethodField()
147     columns_url = serializers.SerializerMethodField()
148     type_suggestions_url = serializers.SerializerMethodField()
149     previews_url = serializers.SerializerMethodField()
150     name = serializers.CharField(required=False, allow_blank=True, default='')
151     data_files = serializers.PrimaryKeyRelatedField(
152         required=False, many=True, queryset=DataFile.objects.all()
153     )
154 
155     class Meta:
156         model = Table
157         fields = ['id', 'name', 'schema', 'created_at', 'updated_at', 'import_verified',
158                   'columns', 'records_url', 'constraints_url', 'columns_url',
159                   'type_suggestions_url', 'previews_url', 'data_files',
160                   'has_dependencies']
161 
162     def get_records_url(self, obj):
163         if isinstance(obj, Table):
164             # Only get records if we are serializing an existing table
165             request = self.context['request']
166             return request.build_absolute_uri(reverse('table-record-list', kwargs={'table_pk': obj.pk}))
167         else:
168             return None
169 
170     def get_constraints_url(self, obj):
171         if isinstance(obj, Table):
172             # Only get constraints if we are serializing an existing table
173             request = self.context['request']
174             return request.build_absolute_uri(reverse('table-constraint-list', kwargs={'table_pk': obj.pk}))
175         else:
176             return None
177 
178     def get_columns_url(self, obj):
179         if isinstance(obj, Table):
180             # Only get columns if we are serializing an existing table
181             request = self.context['request']
182             return request.build_absolute_uri(reverse('table-column-list', kwargs={'table_pk': obj.pk}))
183         else:
184             return None
185 
186     def get_type_suggestions_url(self, obj):
187         if isinstance(obj, Table):
188             # Only get type suggestions if we are serializing an existing table
189             request = self.context['request']
190             return request.build_absolute_uri(reverse('table-type-suggestions', kwargs={'pk': obj.pk}))
191         else:
192             return None
193 
194     def get_previews_url(self, obj):
195         if isinstance(obj, Table):
196             # Only get previews if we are serializing an existing table
197             request = self.context['request']
198             return request.build_absolute_uri(reverse('table-previews', kwargs={'pk': obj.pk}))
199         else:
200             return None
201 
202     def validate_data_files(self, data_files):
203         if data_files and len(data_files) > 1:
204             raise ValidationError('Multiple data files are unsupported.')
205         return data_files
206 
207 
208 class RecordSerializer(serializers.BaseSerializer):
209     def to_representation(self, instance):
210         return instance._asdict()
211 
212 
213 class TablePreviewSerializer(serializers.Serializer):
214     name = serializers.CharField(required=False)
215     columns = SimpleColumnSerializer(many=True)
216 
217 
218 class RecordListParameterSerializer(serializers.Serializer):
219     filters = serializers.JSONField(required=False, default=[])
220     order_by = serializers.JSONField(required=False, default=[])
221     group_count_by = serializers.JSONField(required=False, default=[])
222 
223 
224 class TypeSerializer(serializers.Serializer):
225     identifier = serializers.CharField()
226     name = serializers.CharField()
227     db_types = serializers.ListField(child=serializers.CharField())
228 
229 
230 class DatabaseSerializer(serializers.ModelSerializer):
231     supported_types_url = serializers.SerializerMethodField()
232 
233     class Meta:
234         model = Database
235         fields = ['id', 'name', 'deleted', 'supported_types_url']
236         read_only_fields = ['id', 'name', 'deleted', 'supported_types_url']
237 
238     def get_supported_types_url(self, obj):
239         if isinstance(obj, Database):
240             # Only get records if we are serializing an existing table
241             request = self.context['request']
242             return request.build_absolute_uri(reverse('database-types', kwargs={'pk': obj.pk}))
243         else:
244             return None
245 
246 
247 class DataFileSerializer(serializers.ModelSerializer):
248     user = serializers.PrimaryKeyRelatedField(
249         default=serializers.CurrentUserDefault(), read_only=True
250     )
251     header = serializers.BooleanField(default=True)
252     paste = serializers.CharField(required=False, trim_whitespace=False)
253     url = serializers.URLField(required=False)
254 
255     class Meta:
256         model = DataFile
257         fields = [
258             'id', 'file', 'table_imported_to', 'user', 'header', 'delimiter',
259             'escapechar', 'quotechar', 'paste', 'url', 'created_from'
260         ]
261         extra_kwargs = {
262             'file': {'required': False},
263             'delimiter': {'trim_whitespace': False},
264             'escapechar': {'trim_whitespace': False},
265             'quotechar': {'trim_whitespace': False}
266         }
267         # We only currently support importing to a new table, so setting a table via API is invalid.
268         # User should be set automatically, not submitted via the API.
269         read_only_fields = ['user', 'table_imported_to', 'created_from']
270         write_only_fields = ['paste', 'url']
271 
272     def save(self, **kwargs):
273         """
274         Set user to current user while saving the data file.
275         """
276         current_user = self.fields['user'].get_default()
277         if current_user.is_authenticated:
278             kwargs['user'] = current_user
279         return super().save(**kwargs)
280 
281     def validate(self, data):
282         if not self.partial:
283             # Only perform validation on source files when we're not partial
284             source_fields = ['file', 'paste', 'url']
285             present_fields = [field for field in source_fields if field in data]
286             if len(present_fields) > 1:
287                 raise ValidationError(
288                     f'Multiple source fields passed: {present_fields}.'
289                     f' Only one of {source_fields} should be specified.'
290                 )
291             elif len(present_fields) == 0:
292                 raise ValidationError(
293                     f'One of {source_fields} should be specified.'
294                 )
295         return data
296 
297     def validate_url(self, url):
298         try:
299             response = requests.head(url, allow_redirects=True)
300         except requests.exceptions.ConnectionError:
301             raise ValidationError('URL cannot be reached.')
302 
303         content_type = response.headers.get('content-type')
304         if content_type not in SUPPORTED_URL_CONTENT_TYPES:
305             raise ValidationError(f"URL resource '{content_type}' not a valid type.")
306         return url
307 
308 
309 class ConstraintSerializer(serializers.ModelSerializer):
310     name = serializers.CharField(required=False)
311     type = serializers.CharField()
312     columns = serializers.ListField()
313 
314     class Meta:
315         model = Constraint
316         fields = ['id', 'name', 'type', 'columns']
317
```

Path: `mathesar/filters.py`
Content:
```
1 from django_filters import BooleanFilter, DateTimeFromToRangeFilter
2 from django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter
3 
4 from mathesar.models import Schema, Table, Database
5 
6 
7 class CharInFilter(PropertyBaseInFilter, PropertyCharFilter):
8     pass
9 
10 
11 class SchemaFilter(PropertyFilterSet):
12     database = CharInFilter(field_name='database__name', lookup_expr='in')
13     name = CharInFilter(field_name='name', lookup_expr='in')
14 
15     class Meta:
16         model = Schema
17         fields = ['name']
18 
19 
20 class TableFilter(PropertyFilterSet):
21     name = CharInFilter(field_name='name', lookup_expr='in')
22     created = DateTimeFromToRangeFilter(field_name='created_at')
23     updated = DateTimeFromToRangeFilter(field_name='updated_at')
24     not_imported = BooleanFilter(lookup_expr="isnull", field_name='import_verified')
25 
26     class Meta:
27         model = Table
28         fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']
29 
30 
31 class DatabaseFilter(PropertyFilterSet):
32     class Meta:
33         model = Database
34         fields = ['deleted']
35
```
--- 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/filters.py b/mathesar/filters.py
--- a/mathesar/filters.py
+++ b/mathesar/filters.py
@@ -1,8 +1,25 @@
 from django_filters import BooleanFilter, DateTimeFromToRangeFilter
 from django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter
 
+from mathesar.database.types import MathesarTypeIdentifier
 from mathesar.models import Schema, Table, Database
 
+FILTER_OPTIONS_BY_TYPE_IDENTIFIER = {
+    MathesarTypeIdentifier.BOOLEAN.value:
+    {
+        "db_type": "BOOLEAN",
+        "options": [{
+            "op": "eq",
+            "value": {
+                "allowed_types": ["BOOLEAN"],
+            }
+        }, {
+            "op": "is_null",
+            "value": "null",
+        }]
+    }
+}
+
 
 class CharInFilter(PropertyBaseInFilter, PropertyCharFilter):
     pass
diff --git a/mathesar/serializers.py b/mathesar/serializers.py
--- a/mathesar/serializers.py
+++ b/mathesar/serializers.py
@@ -6,6 +6,7 @@
 from rest_framework.fields import empty
 from rest_framework.settings import api_settings
 
+from mathesar.filters import FILTER_OPTIONS_BY_TYPE_IDENTIFIER
 from mathesar.models import Table, Schema, DataFile, Database, Constraint
 
 
@@ -225,6 +226,10 @@
     identifier = serializers.CharField()
     name = serializers.CharField()
     db_types = serializers.ListField(child=serializers.CharField())
+    filters = serializers.SerializerMethodField()
+
+    def get_filters(self, obj):
+        return FILTER_OPTIONS_BY_TYPE_IDENTIFIER.get(obj.get('identifier'))
 
 
 class DatabaseSerializer(serializers.ModelSerializer):

{"golden_diff": "diff --git a/mathesar/filters.py b/mathesar/filters.py\n--- a/mathesar/filters.py\n+++ b/mathesar/filters.py\n@@ -1,8 +1,25 @@\n from django_filters import BooleanFilter, DateTimeFromToRangeFilter\n from django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter\n \n+from mathesar.database.types import MathesarTypeIdentifier\n from mathesar.models import Schema, Table, Database\n \n+FILTER_OPTIONS_BY_TYPE_IDENTIFIER = {\n+    MathesarTypeIdentifier.BOOLEAN.value:\n+    {\n+        \"db_type\": \"BOOLEAN\",\n+        \"options\": [{\n+            \"op\": \"eq\",\n+            \"value\": {\n+                \"allowed_types\": [\"BOOLEAN\"],\n+            }\n+        }, {\n+            \"op\": \"is_null\",\n+            \"value\": \"null\",\n+        }]\n+    }\n+}\n+\n \n class CharInFilter(PropertyBaseInFilter, PropertyCharFilter):\n     pass\ndiff --git a/mathesar/serializers.py b/mathesar/serializers.py\n--- a/mathesar/serializers.py\n+++ b/mathesar/serializers.py\n@@ -6,6 +6,7 @@\n from rest_framework.fields import empty\n from rest_framework.settings import api_settings\n \n+from mathesar.filters import FILTER_OPTIONS_BY_TYPE_IDENTIFIER\n from mathesar.models import Table, Schema, DataFile, Database, Constraint\n \n \n@@ -225,6 +226,10 @@\n     identifier = serializers.CharField()\n     name = serializers.CharField()\n     db_types = serializers.ListField(child=serializers.CharField())\n+    filters = serializers.SerializerMethodField()\n+\n+    def get_filters(self, obj):\n+        return FILTER_OPTIONS_BY_TYPE_IDENTIFIER.get(obj.get('identifier'))\n \n \n class DatabaseSerializer(serializers.ModelSerializer):\n", "issue": "Implement filtering options for Boolean types\n## Problem\r\nWe need to ensure that records that include columns of Boolean type support the following filters via API: \r\n- is {true/false}\r\n- is not {true/false}\r\n- is empty\r\n- is not empty\r\n\r\nThis may already be done, please verify that it works and add some test cases specifically for boolean data types.\r\n\r\nThis involves:\r\n- Implementing the filters in the backend\r\n- Updating the `/api/v0/databases/<id>/types/` endpoint to store available filters on this type\r\n  - Filter information should include the number of parameters needing to be passed in (e.g. `is` needs 1 parameter, `is empty` needs 0)\r\n\r\n## Additional context\r\n- We're using our fork of `sqlalchemy-filters` to provide filtering. See: https://github.com/centerofci/sqlalchemy-filters.\r\n- See also: #387\r\n- #557 provides some context on storing filters.\n", "before_files": [{"content": "import requests\n\nfrom django.urls import reverse\nfrom rest_framework import serializers\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import empty\nfrom rest_framework.settings import api_settings\n\nfrom mathesar.models import Table, Schema, DataFile, Database, Constraint\n\n\nSUPPORTED_URL_CONTENT_TYPES = {'text/csv', 'text/plain'}\n\n\nclass ModelNameField(serializers.CharField):\n    \"\"\"\n    De-serializes the request field as a string, but serializes the response field as\n    `model.name`. Required to support passing and returing a model name from the\n    endpoint, while also storing the model as a related field.\n    \"\"\"\n    def to_representation(self, value):\n        return value.name\n\n\nclass InputValueField(serializers.CharField):\n    \"\"\"\n    Takes in an arbitrary value. Use to emulate our column and record creation and\n    update endpoints, which handle arbitrary data pulled from request.data\n    \"\"\"\n    def to_internal_value(self, data):\n        return data\n\n    def to_representation(self, value):\n        return value\n\n\nclass NestedTableSerializer(serializers.HyperlinkedModelSerializer):\n    url = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Table\n        fields = ['id', 'name', 'url']\n\n    def get_url(self, obj):\n        request = self.context['request']\n        return request.build_absolute_uri(reverse('table-detail', kwargs={'pk': obj.pk}))\n\n\nclass SchemaSerializer(serializers.HyperlinkedModelSerializer):\n    name = serializers.CharField()\n    database = ModelNameField(max_length=128)\n\n    class Meta:\n        model = Schema\n        fields = ['id', 'name', 'database', 'has_dependencies']\n\n\nclass TypeOptionSerializer(serializers.Serializer):\n    precision = serializers.IntegerField(required=False)\n    scale = serializers.IntegerField(required=False)\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:\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\nclass SimpleColumnSerializer(serializers.Serializer):\n    name = serializers.CharField()\n    type = serializers.CharField(source='plain_type')\n    type_options = TypeOptionSerializer(required=False, allow_null=True)\n\n\nclass ColumnSerializer(SimpleColumnSerializer):\n    name = serializers.CharField(required=False)\n\n    # From scratch fields\n    type = serializers.CharField(source='plain_type', required=False)\n    nullable = serializers.BooleanField(default=True)\n    primary_key = serializers.BooleanField(default=False)\n\n    # From duplication fields\n    source_column = serializers.IntegerField(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    index = serializers.IntegerField(source='column_index', read_only=True)\n    valid_target_types = serializers.ListField(read_only=True)\n    default = InputValueField(\n        source='default_value', read_only=False, default=None, allow_null=True\n    )\n\n    def validate(self, data):\n        if not self.partial:\n            from_scratch_required_fields = ['name', 'type']\n            from_scratch_specific_fields = ['type', '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\nclass TableSerializer(serializers.ModelSerializer):\n    columns = SimpleColumnSerializer(many=True, source='sa_columns', required=False)\n    records_url = serializers.SerializerMethodField()\n    constraints_url = serializers.SerializerMethodField()\n    columns_url = serializers.SerializerMethodField()\n    type_suggestions_url = serializers.SerializerMethodField()\n    previews_url = serializers.SerializerMethodField()\n    name = serializers.CharField(required=False, allow_blank=True, default='')\n    data_files = serializers.PrimaryKeyRelatedField(\n        required=False, many=True, queryset=DataFile.objects.all()\n    )\n\n    class Meta:\n        model = Table\n        fields = ['id', 'name', 'schema', 'created_at', 'updated_at', 'import_verified',\n                  'columns', 'records_url', 'constraints_url', 'columns_url',\n                  'type_suggestions_url', 'previews_url', 'data_files',\n                  'has_dependencies']\n\n    def get_records_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get records if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-record-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_constraints_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get constraints if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-constraint-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_columns_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get columns if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-column-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_type_suggestions_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get type suggestions if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-type-suggestions', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n    def get_previews_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get previews if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-previews', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n    def validate_data_files(self, data_files):\n        if data_files and len(data_files) > 1:\n            raise ValidationError('Multiple data files are unsupported.')\n        return data_files\n\n\nclass RecordSerializer(serializers.BaseSerializer):\n    def to_representation(self, instance):\n        return instance._asdict()\n\n\nclass TablePreviewSerializer(serializers.Serializer):\n    name = serializers.CharField(required=False)\n    columns = SimpleColumnSerializer(many=True)\n\n\nclass RecordListParameterSerializer(serializers.Serializer):\n    filters = serializers.JSONField(required=False, default=[])\n    order_by = serializers.JSONField(required=False, default=[])\n    group_count_by = serializers.JSONField(required=False, default=[])\n\n\nclass TypeSerializer(serializers.Serializer):\n    identifier = serializers.CharField()\n    name = serializers.CharField()\n    db_types = serializers.ListField(child=serializers.CharField())\n\n\nclass DatabaseSerializer(serializers.ModelSerializer):\n    supported_types_url = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Database\n        fields = ['id', 'name', 'deleted', 'supported_types_url']\n        read_only_fields = ['id', 'name', 'deleted', 'supported_types_url']\n\n    def get_supported_types_url(self, obj):\n        if isinstance(obj, Database):\n            # Only get records if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('database-types', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n\nclass DataFileSerializer(serializers.ModelSerializer):\n    user = serializers.PrimaryKeyRelatedField(\n        default=serializers.CurrentUserDefault(), read_only=True\n    )\n    header = serializers.BooleanField(default=True)\n    paste = serializers.CharField(required=False, trim_whitespace=False)\n    url = serializers.URLField(required=False)\n\n    class Meta:\n        model = DataFile\n        fields = [\n            'id', 'file', 'table_imported_to', 'user', 'header', 'delimiter',\n            'escapechar', 'quotechar', 'paste', 'url', 'created_from'\n        ]\n        extra_kwargs = {\n            'file': {'required': False},\n            'delimiter': {'trim_whitespace': False},\n            'escapechar': {'trim_whitespace': False},\n            'quotechar': {'trim_whitespace': False}\n        }\n        # We only currently support importing to a new table, so setting a table via API is invalid.\n        # User should be set automatically, not submitted via the API.\n        read_only_fields = ['user', 'table_imported_to', 'created_from']\n        write_only_fields = ['paste', 'url']\n\n    def save(self, **kwargs):\n        \"\"\"\n        Set user to current user while saving the data file.\n        \"\"\"\n        current_user = self.fields['user'].get_default()\n        if current_user.is_authenticated:\n            kwargs['user'] = current_user\n        return super().save(**kwargs)\n\n    def validate(self, data):\n        if not self.partial:\n            # Only perform validation on source files when we're not partial\n            source_fields = ['file', 'paste', 'url']\n            present_fields = [field for field in source_fields if field in data]\n            if len(present_fields) > 1:\n                raise ValidationError(\n                    f'Multiple source fields passed: {present_fields}.'\n                    f' Only one of {source_fields} should be specified.'\n                )\n            elif len(present_fields) == 0:\n                raise ValidationError(\n                    f'One of {source_fields} should be specified.'\n                )\n        return data\n\n    def validate_url(self, url):\n        try:\n            response = requests.head(url, allow_redirects=True)\n        except requests.exceptions.ConnectionError:\n            raise ValidationError('URL cannot be reached.')\n\n        content_type = response.headers.get('content-type')\n        if content_type not in SUPPORTED_URL_CONTENT_TYPES:\n            raise ValidationError(f\"URL resource '{content_type}' not a valid type.\")\n        return url\n\n\nclass ConstraintSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(required=False)\n    type = serializers.CharField()\n    columns = serializers.ListField()\n\n    class Meta:\n        model = Constraint\n        fields = ['id', 'name', 'type', 'columns']\n", "path": "mathesar/serializers.py"}, {"content": "from django_filters import BooleanFilter, DateTimeFromToRangeFilter\nfrom django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter\n\nfrom mathesar.models import Schema, Table, Database\n\n\nclass CharInFilter(PropertyBaseInFilter, PropertyCharFilter):\n    pass\n\n\nclass SchemaFilter(PropertyFilterSet):\n    database = CharInFilter(field_name='database__name', lookup_expr='in')\n    name = CharInFilter(field_name='name', lookup_expr='in')\n\n    class Meta:\n        model = Schema\n        fields = ['name']\n\n\nclass TableFilter(PropertyFilterSet):\n    name = CharInFilter(field_name='name', lookup_expr='in')\n    created = DateTimeFromToRangeFilter(field_name='created_at')\n    updated = DateTimeFromToRangeFilter(field_name='updated_at')\n    not_imported = BooleanFilter(lookup_expr=\"isnull\", field_name='import_verified')\n\n    class Meta:\n        model = Table\n        fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']\n\n\nclass DatabaseFilter(PropertyFilterSet):\n    class Meta:\n        model = Database\n        fields = ['deleted']\n", "path": "mathesar/filters.py"}], "after_files": [{"content": "import requests\n\nfrom django.urls import reverse\nfrom rest_framework import serializers\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.fields import empty\nfrom rest_framework.settings import api_settings\n\nfrom mathesar.filters import FILTER_OPTIONS_BY_TYPE_IDENTIFIER\nfrom mathesar.models import Table, Schema, DataFile, Database, Constraint\n\n\nSUPPORTED_URL_CONTENT_TYPES = {'text/csv', 'text/plain'}\n\n\nclass ModelNameField(serializers.CharField):\n    \"\"\"\n    De-serializes the request field as a string, but serializes the response field as\n    `model.name`. Required to support passing and returing a model name from the\n    endpoint, while also storing the model as a related field.\n    \"\"\"\n    def to_representation(self, value):\n        return value.name\n\n\nclass InputValueField(serializers.CharField):\n    \"\"\"\n    Takes in an arbitrary value. Use to emulate our column and record creation and\n    update endpoints, which handle arbitrary data pulled from request.data\n    \"\"\"\n    def to_internal_value(self, data):\n        return data\n\n    def to_representation(self, value):\n        return value\n\n\nclass NestedTableSerializer(serializers.HyperlinkedModelSerializer):\n    url = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Table\n        fields = ['id', 'name', 'url']\n\n    def get_url(self, obj):\n        request = self.context['request']\n        return request.build_absolute_uri(reverse('table-detail', kwargs={'pk': obj.pk}))\n\n\nclass SchemaSerializer(serializers.HyperlinkedModelSerializer):\n    name = serializers.CharField()\n    database = ModelNameField(max_length=128)\n\n    class Meta:\n        model = Schema\n        fields = ['id', 'name', 'database', 'has_dependencies']\n\n\nclass TypeOptionSerializer(serializers.Serializer):\n    precision = serializers.IntegerField(required=False)\n    scale = serializers.IntegerField(required=False)\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:\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\nclass SimpleColumnSerializer(serializers.Serializer):\n    name = serializers.CharField()\n    type = serializers.CharField(source='plain_type')\n    type_options = TypeOptionSerializer(required=False, allow_null=True)\n\n\nclass ColumnSerializer(SimpleColumnSerializer):\n    name = serializers.CharField(required=False)\n\n    # From scratch fields\n    type = serializers.CharField(source='plain_type', required=False)\n    nullable = serializers.BooleanField(default=True)\n    primary_key = serializers.BooleanField(default=False)\n\n    # From duplication fields\n    source_column = serializers.IntegerField(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    index = serializers.IntegerField(source='column_index', read_only=True)\n    valid_target_types = serializers.ListField(read_only=True)\n    default = InputValueField(\n        source='default_value', read_only=False, default=None, allow_null=True\n    )\n\n    def validate(self, data):\n        if not self.partial:\n            from_scratch_required_fields = ['name', 'type']\n            from_scratch_specific_fields = ['type', '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\nclass TableSerializer(serializers.ModelSerializer):\n    columns = SimpleColumnSerializer(many=True, source='sa_columns', required=False)\n    records_url = serializers.SerializerMethodField()\n    constraints_url = serializers.SerializerMethodField()\n    columns_url = serializers.SerializerMethodField()\n    type_suggestions_url = serializers.SerializerMethodField()\n    previews_url = serializers.SerializerMethodField()\n    name = serializers.CharField(required=False, allow_blank=True, default='')\n    data_files = serializers.PrimaryKeyRelatedField(\n        required=False, many=True, queryset=DataFile.objects.all()\n    )\n\n    class Meta:\n        model = Table\n        fields = ['id', 'name', 'schema', 'created_at', 'updated_at', 'import_verified',\n                  'columns', 'records_url', 'constraints_url', 'columns_url',\n                  'type_suggestions_url', 'previews_url', 'data_files',\n                  'has_dependencies']\n\n    def get_records_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get records if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-record-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_constraints_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get constraints if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-constraint-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_columns_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get columns if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-column-list', kwargs={'table_pk': obj.pk}))\n        else:\n            return None\n\n    def get_type_suggestions_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get type suggestions if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-type-suggestions', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n    def get_previews_url(self, obj):\n        if isinstance(obj, Table):\n            # Only get previews if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('table-previews', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n    def validate_data_files(self, data_files):\n        if data_files and len(data_files) > 1:\n            raise ValidationError('Multiple data files are unsupported.')\n        return data_files\n\n\nclass RecordSerializer(serializers.BaseSerializer):\n    def to_representation(self, instance):\n        return instance._asdict()\n\n\nclass TablePreviewSerializer(serializers.Serializer):\n    name = serializers.CharField(required=False)\n    columns = SimpleColumnSerializer(many=True)\n\n\nclass RecordListParameterSerializer(serializers.Serializer):\n    filters = serializers.JSONField(required=False, default=[])\n    order_by = serializers.JSONField(required=False, default=[])\n    group_count_by = serializers.JSONField(required=False, default=[])\n\n\nclass TypeSerializer(serializers.Serializer):\n    identifier = serializers.CharField()\n    name = serializers.CharField()\n    db_types = serializers.ListField(child=serializers.CharField())\n    filters = serializers.SerializerMethodField()\n\n    def get_filters(self, obj):\n        return FILTER_OPTIONS_BY_TYPE_IDENTIFIER.get(obj.get('identifier'))\n\n\nclass DatabaseSerializer(serializers.ModelSerializer):\n    supported_types_url = serializers.SerializerMethodField()\n\n    class Meta:\n        model = Database\n        fields = ['id', 'name', 'deleted', 'supported_types_url']\n        read_only_fields = ['id', 'name', 'deleted', 'supported_types_url']\n\n    def get_supported_types_url(self, obj):\n        if isinstance(obj, Database):\n            # Only get records if we are serializing an existing table\n            request = self.context['request']\n            return request.build_absolute_uri(reverse('database-types', kwargs={'pk': obj.pk}))\n        else:\n            return None\n\n\nclass DataFileSerializer(serializers.ModelSerializer):\n    user = serializers.PrimaryKeyRelatedField(\n        default=serializers.CurrentUserDefault(), read_only=True\n    )\n    header = serializers.BooleanField(default=True)\n    paste = serializers.CharField(required=False, trim_whitespace=False)\n    url = serializers.URLField(required=False)\n\n    class Meta:\n        model = DataFile\n        fields = [\n            'id', 'file', 'table_imported_to', 'user', 'header', 'delimiter',\n            'escapechar', 'quotechar', 'paste', 'url', 'created_from'\n        ]\n        extra_kwargs = {\n            'file': {'required': False},\n            'delimiter': {'trim_whitespace': False},\n            'escapechar': {'trim_whitespace': False},\n            'quotechar': {'trim_whitespace': False}\n        }\n        # We only currently support importing to a new table, so setting a table via API is invalid.\n        # User should be set automatically, not submitted via the API.\n        read_only_fields = ['user', 'table_imported_to', 'created_from']\n        write_only_fields = ['paste', 'url']\n\n    def save(self, **kwargs):\n        \"\"\"\n        Set user to current user while saving the data file.\n        \"\"\"\n        current_user = self.fields['user'].get_default()\n        if current_user.is_authenticated:\n            kwargs['user'] = current_user\n        return super().save(**kwargs)\n\n    def validate(self, data):\n        if not self.partial:\n            # Only perform validation on source files when we're not partial\n            source_fields = ['file', 'paste', 'url']\n            present_fields = [field for field in source_fields if field in data]\n            if len(present_fields) > 1:\n                raise ValidationError(\n                    f'Multiple source fields passed: {present_fields}.'\n                    f' Only one of {source_fields} should be specified.'\n                )\n            elif len(present_fields) == 0:\n                raise ValidationError(\n                    f'One of {source_fields} should be specified.'\n                )\n        return data\n\n    def validate_url(self, url):\n        try:\n            response = requests.head(url, allow_redirects=True)\n        except requests.exceptions.ConnectionError:\n            raise ValidationError('URL cannot be reached.')\n\n        content_type = response.headers.get('content-type')\n        if content_type not in SUPPORTED_URL_CONTENT_TYPES:\n            raise ValidationError(f\"URL resource '{content_type}' not a valid type.\")\n        return url\n\n\nclass ConstraintSerializer(serializers.ModelSerializer):\n    name = serializers.CharField(required=False)\n    type = serializers.CharField()\n    columns = serializers.ListField()\n\n    class Meta:\n        model = Constraint\n        fields = ['id', 'name', 'type', 'columns']\n", "path": "mathesar/serializers.py"}, {"content": "from django_filters import BooleanFilter, DateTimeFromToRangeFilter\nfrom django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter\n\nfrom mathesar.database.types import MathesarTypeIdentifier\nfrom mathesar.models import Schema, Table, Database\n\nFILTER_OPTIONS_BY_TYPE_IDENTIFIER = {\n    MathesarTypeIdentifier.BOOLEAN.value:\n    {\n        \"db_type\": \"BOOLEAN\",\n        \"options\": [{\n            \"op\": \"eq\",\n            \"value\": {\n                \"allowed_types\": [\"BOOLEAN\"],\n            }\n        }, {\n            \"op\": \"is_null\",\n            \"value\": \"null\",\n        }]\n    }\n}\n\n\nclass CharInFilter(PropertyBaseInFilter, PropertyCharFilter):\n    pass\n\n\nclass SchemaFilter(PropertyFilterSet):\n    database = CharInFilter(field_name='database__name', lookup_expr='in')\n    name = CharInFilter(field_name='name', lookup_expr='in')\n\n    class Meta:\n        model = Schema\n        fields = ['name']\n\n\nclass TableFilter(PropertyFilterSet):\n    name = CharInFilter(field_name='name', lookup_expr='in')\n    created = DateTimeFromToRangeFilter(field_name='created_at')\n    updated = DateTimeFromToRangeFilter(field_name='updated_at')\n    not_imported = BooleanFilter(lookup_expr=\"isnull\", field_name='import_verified')\n\n    class Meta:\n        model = Table\n        fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']\n\n\nclass DatabaseFilter(PropertyFilterSet):\n    class Meta:\n        model = Database\n        fields = ['deleted']\n", "path": "mathesar/filters.py"}]}