prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  More filters for table list API
  **Problem**
  <!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->
  The table list API should allow filtering. For example, we might want to get the list of all tables in a schema to see if the table the user is trying to create already exists in that schema.
  
  **Proposed solution**
  <!-- A clear and concise description of your proposed solution or feature. -->
  The table list endpoint should support filtering by:
  - schema
  - before/after: created, last updated
  - whether the import was verified
  
  **Additional context**
  <!-- Add any other context or screenshots about the feature request here.-->
  We should use `django-filter` since it integrates with DRF and makes setting up filters easy.
  
  --- 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/models.py`
  Content:
  ```
  1 from django.contrib.auth.models import User
  2 from django.core.cache import cache
  3 from django.db import models
  4 from django.utils.functional import cached_property
  5 
  6 from mathesar.database.base import create_mathesar_engine
  7 from mathesar.utils import models as model_utils
  8 from db import tables, records, schemas
  9 
  10 NAME_CACHE_INTERVAL = 60 * 5
  11 
  12 
  13 class BaseModel(models.Model):
  14     created_at = models.DateTimeField(auto_now_add=True)
  15     updated_at = models.DateTimeField(auto_now=True)
  16 
  17     class Meta:
  18         abstract = True
  19 
  20 
  21 class DatabaseObject(BaseModel):
  22     oid = models.IntegerField()
  23 
  24     class Meta:
  25         abstract = True
  26 
  27     def __str__(self):
  28         return f"{self.__class__.__name__}: {self.oid}"
  29 
  30 
  31 class Schema(DatabaseObject):
  32     database = models.CharField(max_length=128)
  33 
  34     @cached_property
  35     def _sa_engine(self):
  36         # We're caching this since the engine is used frequently.
  37         return create_mathesar_engine(self.database)
  38 
  39     @cached_property
  40     def name(self):
  41         cache_key = f"{self.database}_schema_name_{self.oid}"
  42         try:
  43             schema_name = cache.get(cache_key)
  44             if schema_name is None:
  45                 schema_name = schemas.get_schema_name_from_oid(
  46                     self.oid, self._sa_engine
  47                 )
  48                 cache.set(cache_key, schema_name, NAME_CACHE_INTERVAL)
  49             return schema_name
  50         # We catch this error, since it lets us decouple the cadence of
  51         # overall DB reflection from the cadence of cache expiration for
  52         # schema names.  Also, it makes it obvious when the DB layer has
  53         # been altered, as opposed to other reasons for a 404 when
  54         # requesting a schema.
  55         except TypeError:
  56             return 'MISSING'
  57 
  58 
  59 class Table(DatabaseObject):
  60     schema = models.ForeignKey('Schema', on_delete=models.CASCADE,
  61                                related_name='tables')
  62     import_verified = models.BooleanField(blank=True, null=True)
  63 
  64     @cached_property
  65     def _sa_table(self):
  66         try:
  67             table = tables.reflect_table_from_oid(
  68                 self.oid, self.schema._sa_engine,
  69             )
  70         # We catch this error, since it lets us decouple the cadence of
  71         # overall DB reflection from the cadence of cache expiration for
  72         # table names.  Also, it makes it obvious when the DB layer has
  73         # been altered, as opposed to other reasons for a 404 when
  74         # requesting a table.
  75         except TypeError:
  76             table = tables.create_empty_table("MISSING")
  77         return table
  78 
  79     @cached_property
  80     def name(self):
  81         return self._sa_table.name
  82 
  83     @property
  84     def sa_columns(self):
  85         return self._sa_table.columns
  86 
  87     @property
  88     def sa_column_names(self):
  89         return self.sa_columns.keys()
  90 
  91     @property
  92     def sa_num_records(self):
  93         return tables.get_count(self._sa_table, self.schema._sa_engine)
  94 
  95     @property
  96     def sa_all_records(self):
  97         return records.get_records(self._sa_table, self.schema._sa_engine)
  98 
  99     def get_record(self, id_value):
  100         return records.get_record(self._sa_table, self.schema._sa_engine, id_value)
  101 
  102     def get_records(self, limit=None, offset=None):
  103         return records.get_records(self._sa_table, self.schema._sa_engine, limit, offset)
  104 
  105     def create_record_or_records(self, record_data):
  106         return records.create_record_or_records(self._sa_table, self.schema._sa_engine, record_data)
  107 
  108     def update_record(self, id_value, record_data):
  109         return records.update_record(self._sa_table, self.schema._sa_engine, id_value, record_data)
  110 
  111     def delete_record(self, id_value):
  112         return records.delete_record(self._sa_table, self.schema._sa_engine, id_value)
  113 
  114 
  115 class DataFile(BaseModel):
  116     file = models.FileField(
  117         upload_to=model_utils.user_directory_path,
  118     )
  119     user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
  120     table_imported_to = models.ForeignKey(Table, related_name="data_files", blank=True,
  121                                           null=True, on_delete=models.SET_NULL)
  122     delimiter = models.CharField(max_length=1, default=',', blank=True)
  123     escapechar = models.CharField(max_length=1, blank=True)
  124     quotechar = models.CharField(max_length=1, default='"', blank=True)
  125
  ```
  
  Path: `mathesar/filters.py`
  Content:
  ```
  1 from django_property_filter import (
  2     PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,
  3 )
  4 
  5 from mathesar.models import Schema, Table
  6 
  7 
  8 class CharInFilter(PropertyBaseInFilter, PropertyCharFilter):
  9     pass
  10 
  11 
  12 class SchemaFilter(PropertyFilterSet):
  13     database = CharInFilter(field_name='database', lookup_expr='in')
  14     name = CharInFilter(field_name='name', lookup_expr='in')
  15 
  16     class Meta:
  17         model = Schema
  18         fields = ['database', 'name']
  19 
  20 
  21 class TableFilter(PropertyFilterSet):
  22     name = CharInFilter(field_name='name', lookup_expr='in')
  23 
  24     class Meta:
  25         model = Table
  26         fields = ['name']
  27
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/mathesar/filters.py b/mathesar/filters.py
  --- a/mathesar/filters.py
  +++ b/mathesar/filters.py
  @@ -1,5 +1,6 @@
   from django_property_filter import (
       PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,
  +    PropertyDateTimeFromToRangeFilter, PropertyBooleanFilter
   )
   
   from mathesar.models import Schema, Table
  @@ -20,7 +21,13 @@
   
   class TableFilter(PropertyFilterSet):
       name = CharInFilter(field_name='name', lookup_expr='in')
  +    schema = CharInFilter(field_name='schema__name', lookup_expr='in')
  +    created = PropertyDateTimeFromToRangeFilter(field_name='created_at')
  +    updated = PropertyDateTimeFromToRangeFilter(field_name='updated_at')
  +    import_verified = PropertyBooleanFilter(field_name='import_verified')
  +    not_imported = PropertyBooleanFilter(lookup_expr="isnull",
  +                                         field_name='import_verified')
   
       class Meta:
           model = Table
  -        fields = ['name']
  +        fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']
  diff --git a/mathesar/models.py b/mathesar/models.py
  --- a/mathesar/models.py
  +++ b/mathesar/models.py
  @@ -28,13 +28,21 @@
           return f"{self.__class__.__name__}: {self.oid}"
   
   
  +# TODO: Replace with a proper form of caching
  +# See: https://github.com/centerofci/mathesar/issues/280
  +_engine = None
  +
  +
   class Schema(DatabaseObject):
       database = models.CharField(max_length=128)
   
  -    @cached_property
  +    @property
       def _sa_engine(self):
  +        global _engine
           # We're caching this since the engine is used frequently.
  -        return create_mathesar_engine(self.database)
  +        if _engine is None:
  +            _engine = create_mathesar_engine(self.database)
  +        return _engine
   
       @cached_property
       def name(self):
  
verification_info: {"golden_diff": "diff --git a/mathesar/filters.py b/mathesar/filters.py\n--- a/mathesar/filters.py\n+++ b/mathesar/filters.py\n@@ -1,5 +1,6 @@\n from django_property_filter import (\n     PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,\n+    PropertyDateTimeFromToRangeFilter, PropertyBooleanFilter\n )\n \n from mathesar.models import Schema, Table\n@@ -20,7 +21,13 @@\n \n class TableFilter(PropertyFilterSet):\n     name = CharInFilter(field_name='name', lookup_expr='in')\n+    schema = CharInFilter(field_name='schema__name', lookup_expr='in')\n+    created = PropertyDateTimeFromToRangeFilter(field_name='created_at')\n+    updated = PropertyDateTimeFromToRangeFilter(field_name='updated_at')\n+    import_verified = PropertyBooleanFilter(field_name='import_verified')\n+    not_imported = PropertyBooleanFilter(lookup_expr=\"isnull\",\n+                                         field_name='import_verified')\n \n     class Meta:\n         model = Table\n-        fields = ['name']\n+        fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']\ndiff --git a/mathesar/models.py b/mathesar/models.py\n--- a/mathesar/models.py\n+++ b/mathesar/models.py\n@@ -28,13 +28,21 @@\n         return f\"{self.__class__.__name__}: {self.oid}\"\n \n \n+# TODO: Replace with a proper form of caching\n+# See: https://github.com/centerofci/mathesar/issues/280\n+_engine = None\n+\n+\n class Schema(DatabaseObject):\n     database = models.CharField(max_length=128)\n \n-    @cached_property\n+    @property\n     def _sa_engine(self):\n+        global _engine\n         # We're caching this since the engine is used frequently.\n-        return create_mathesar_engine(self.database)\n+        if _engine is None:\n+            _engine = create_mathesar_engine(self.database)\n+        return _engine\n \n     @cached_property\n     def name(self):\n", "issue": "More filters for table list API\n**Problem**\r\n<!-- Please provide a clear and concise description of the problem that this feature request is designed to solve.-->\r\nThe table list API should allow filtering. For example, we might want to get the list of all tables in a schema to see if the table the user is trying to create already exists in that schema.\r\n\r\n**Proposed solution**\r\n<!-- A clear and concise description of your proposed solution or feature. -->\r\nThe table list endpoint should support filtering by:\r\n- schema\r\n- before/after: created, last updated\r\n- whether the import was verified\r\n\r\n**Additional context**\r\n<!-- Add any other context or screenshots about the feature request here.-->\r\nWe should use `django-filter` since it integrates with DRF and makes setting up filters easy.\n", "before_files": [{"content": "from django.contrib.auth.models import User\nfrom django.core.cache import cache\nfrom django.db import models\nfrom django.utils.functional import cached_property\n\nfrom mathesar.database.base import create_mathesar_engine\nfrom mathesar.utils import models as model_utils\nfrom db import tables, records, schemas\n\nNAME_CACHE_INTERVAL = 60 * 5\n\n\nclass BaseModel(models.Model):\n    created_at = models.DateTimeField(auto_now_add=True)\n    updated_at = models.DateTimeField(auto_now=True)\n\n    class Meta:\n        abstract = True\n\n\nclass DatabaseObject(BaseModel):\n    oid = models.IntegerField()\n\n    class Meta:\n        abstract = True\n\n    def __str__(self):\n        return f\"{self.__class__.__name__}: {self.oid}\"\n\n\nclass Schema(DatabaseObject):\n    database = models.CharField(max_length=128)\n\n    @cached_property\n    def _sa_engine(self):\n        # We're caching this since the engine is used frequently.\n        return create_mathesar_engine(self.database)\n\n    @cached_property\n    def name(self):\n        cache_key = f\"{self.database}_schema_name_{self.oid}\"\n        try:\n            schema_name = cache.get(cache_key)\n            if schema_name is None:\n                schema_name = schemas.get_schema_name_from_oid(\n                    self.oid, self._sa_engine\n                )\n                cache.set(cache_key, schema_name, NAME_CACHE_INTERVAL)\n            return schema_name\n        # We catch this error, since it lets us decouple the cadence of\n        # overall DB reflection from the cadence of cache expiration for\n        # schema names.  Also, it makes it obvious when the DB layer has\n        # been altered, as opposed to other reasons for a 404 when\n        # requesting a schema.\n        except TypeError:\n            return 'MISSING'\n\n\nclass Table(DatabaseObject):\n    schema = models.ForeignKey('Schema', on_delete=models.CASCADE,\n                               related_name='tables')\n    import_verified = models.BooleanField(blank=True, null=True)\n\n    @cached_property\n    def _sa_table(self):\n        try:\n            table = tables.reflect_table_from_oid(\n                self.oid, self.schema._sa_engine,\n            )\n        # We catch this error, since it lets us decouple the cadence of\n        # overall DB reflection from the cadence of cache expiration for\n        # table names.  Also, it makes it obvious when the DB layer has\n        # been altered, as opposed to other reasons for a 404 when\n        # requesting a table.\n        except TypeError:\n            table = tables.create_empty_table(\"MISSING\")\n        return table\n\n    @cached_property\n    def name(self):\n        return self._sa_table.name\n\n    @property\n    def sa_columns(self):\n        return self._sa_table.columns\n\n    @property\n    def sa_column_names(self):\n        return self.sa_columns.keys()\n\n    @property\n    def sa_num_records(self):\n        return tables.get_count(self._sa_table, self.schema._sa_engine)\n\n    @property\n    def sa_all_records(self):\n        return records.get_records(self._sa_table, self.schema._sa_engine)\n\n    def get_record(self, id_value):\n        return records.get_record(self._sa_table, self.schema._sa_engine, id_value)\n\n    def get_records(self, limit=None, offset=None):\n        return records.get_records(self._sa_table, self.schema._sa_engine, limit, offset)\n\n    def create_record_or_records(self, record_data):\n        return records.create_record_or_records(self._sa_table, self.schema._sa_engine, record_data)\n\n    def update_record(self, id_value, record_data):\n        return records.update_record(self._sa_table, self.schema._sa_engine, id_value, record_data)\n\n    def delete_record(self, id_value):\n        return records.delete_record(self._sa_table, self.schema._sa_engine, id_value)\n\n\nclass DataFile(BaseModel):\n    file = models.FileField(\n        upload_to=model_utils.user_directory_path,\n    )\n    user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)\n    table_imported_to = models.ForeignKey(Table, related_name=\"data_files\", blank=True,\n                                          null=True, on_delete=models.SET_NULL)\n    delimiter = models.CharField(max_length=1, default=',', blank=True)\n    escapechar = models.CharField(max_length=1, blank=True)\n    quotechar = models.CharField(max_length=1, default='\"', blank=True)\n", "path": "mathesar/models.py"}, {"content": "from django_property_filter import (\n    PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,\n)\n\nfrom mathesar.models import Schema, Table\n\n\nclass CharInFilter(PropertyBaseInFilter, PropertyCharFilter):\n    pass\n\n\nclass SchemaFilter(PropertyFilterSet):\n    database = CharInFilter(field_name='database', lookup_expr='in')\n    name = CharInFilter(field_name='name', lookup_expr='in')\n\n    class Meta:\n        model = Schema\n        fields = ['database', 'name']\n\n\nclass TableFilter(PropertyFilterSet):\n    name = CharInFilter(field_name='name', lookup_expr='in')\n\n    class Meta:\n        model = Table\n        fields = ['name']\n", "path": "mathesar/filters.py"}], "after_files": [{"content": "from django.contrib.auth.models import User\nfrom django.core.cache import cache\nfrom django.db import models\nfrom django.utils.functional import cached_property\n\nfrom mathesar.database.base import create_mathesar_engine\nfrom mathesar.utils import models as model_utils\nfrom db import tables, records, schemas\n\nNAME_CACHE_INTERVAL = 60 * 5\n\n\nclass BaseModel(models.Model):\n    created_at = models.DateTimeField(auto_now_add=True)\n    updated_at = models.DateTimeField(auto_now=True)\n\n    class Meta:\n        abstract = True\n\n\nclass DatabaseObject(BaseModel):\n    oid = models.IntegerField()\n\n    class Meta:\n        abstract = True\n\n    def __str__(self):\n        return f\"{self.__class__.__name__}: {self.oid}\"\n\n\n# TODO: Replace with a proper form of caching\n# See: https://github.com/centerofci/mathesar/issues/280\n_engine = None\n\n\nclass Schema(DatabaseObject):\n    database = models.CharField(max_length=128)\n\n    @property\n    def _sa_engine(self):\n        global _engine\n        # We're caching this since the engine is used frequently.\n        if _engine is None:\n            _engine = create_mathesar_engine(self.database)\n        return _engine\n\n    @cached_property\n    def name(self):\n        cache_key = f\"{self.database}_schema_name_{self.oid}\"\n        try:\n            schema_name = cache.get(cache_key)\n            if schema_name is None:\n                schema_name = schemas.get_schema_name_from_oid(\n                    self.oid, self._sa_engine\n                )\n                cache.set(cache_key, schema_name, NAME_CACHE_INTERVAL)\n            return schema_name\n        # We catch this error, since it lets us decouple the cadence of\n        # overall DB reflection from the cadence of cache expiration for\n        # schema names.  Also, it makes it obvious when the DB layer has\n        # been altered, as opposed to other reasons for a 404 when\n        # requesting a schema.\n        except TypeError:\n            return 'MISSING'\n\n\nclass Table(DatabaseObject):\n    schema = models.ForeignKey('Schema', on_delete=models.CASCADE,\n                               related_name='tables')\n    import_verified = models.BooleanField(blank=True, null=True)\n\n    @cached_property\n    def _sa_table(self):\n        try:\n            table = tables.reflect_table_from_oid(\n                self.oid, self.schema._sa_engine,\n            )\n        # We catch this error, since it lets us decouple the cadence of\n        # overall DB reflection from the cadence of cache expiration for\n        # table names.  Also, it makes it obvious when the DB layer has\n        # been altered, as opposed to other reasons for a 404 when\n        # requesting a table.\n        except TypeError:\n            table = tables.create_empty_table(\"MISSING\")\n        return table\n\n    @cached_property\n    def name(self):\n        return self._sa_table.name\n\n    @property\n    def sa_columns(self):\n        return self._sa_table.columns\n\n    @property\n    def sa_column_names(self):\n        return self.sa_columns.keys()\n\n    @property\n    def sa_num_records(self):\n        return tables.get_count(self._sa_table, self.schema._sa_engine)\n\n    @property\n    def sa_all_records(self):\n        return records.get_records(self._sa_table, self.schema._sa_engine)\n\n    def get_record(self, id_value):\n        return records.get_record(self._sa_table, self.schema._sa_engine, id_value)\n\n    def get_records(self, limit=None, offset=None):\n        return records.get_records(self._sa_table, self.schema._sa_engine, limit, offset)\n\n    def create_record_or_records(self, record_data):\n        return records.create_record_or_records(self._sa_table, self.schema._sa_engine, record_data)\n\n    def update_record(self, id_value, record_data):\n        return records.update_record(self._sa_table, self.schema._sa_engine, id_value, record_data)\n\n    def delete_record(self, id_value):\n        return records.delete_record(self._sa_table, self.schema._sa_engine, id_value)\n\n\nclass DataFile(BaseModel):\n    file = models.FileField(\n        upload_to=model_utils.user_directory_path,\n    )\n    user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)\n    table_imported_to = models.ForeignKey(Table, related_name=\"data_files\", blank=True,\n                                          null=True, on_delete=models.SET_NULL)\n    delimiter = models.CharField(max_length=1, default=',', blank=True)\n    escapechar = models.CharField(max_length=1, blank=True)\n    quotechar = models.CharField(max_length=1, default='\"', blank=True)\n", "path": "mathesar/models.py"}, {"content": "from django_property_filter import (\n    PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,\n    PropertyDateTimeFromToRangeFilter, PropertyBooleanFilter\n)\n\nfrom mathesar.models import Schema, Table\n\n\nclass CharInFilter(PropertyBaseInFilter, PropertyCharFilter):\n    pass\n\n\nclass SchemaFilter(PropertyFilterSet):\n    database = CharInFilter(field_name='database', lookup_expr='in')\n    name = CharInFilter(field_name='name', lookup_expr='in')\n\n    class Meta:\n        model = Schema\n        fields = ['database', 'name']\n\n\nclass TableFilter(PropertyFilterSet):\n    name = CharInFilter(field_name='name', lookup_expr='in')\n    schema = CharInFilter(field_name='schema__name', lookup_expr='in')\n    created = PropertyDateTimeFromToRangeFilter(field_name='created_at')\n    updated = PropertyDateTimeFromToRangeFilter(field_name='updated_at')\n    import_verified = PropertyBooleanFilter(field_name='import_verified')\n    not_imported = PropertyBooleanFilter(lookup_expr=\"isnull\",\n                                         field_name='import_verified')\n\n    class Meta:\n        model = Table\n        fields = ['name', 'schema', 'created_at', 'updated_at', 'import_verified']\n", "path": "mathesar/filters.py"}]}