Repository: mathesar-foundation/mathesar

Issue:
File import preview page
Implementation of the file import preview page based on design spec.

  - Show preview of a small set of rows
  - Allow users to change type of column
  - Allow users to change name of columns
  - Use/Not-use first row as headers
  - Tests


Discussion:
Blocked by #294 
This is still blocked until #506 is merged.
This is now unblocked. Please refer to https://wiki.mathesar.org/engineering/architecture/import-preview-api for the various APIs that will back this page.

Resolving patch:
diff --git a/mathesar/filters.py b/mathesar/filters.py
--- a/mathesar/filters.py
+++ b/mathesar/filters.py
@@ -1,7 +1,5 @@
-from django_property_filter import (
-    PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter,
-    PropertyDateTimeFromToRangeFilter, PropertyBooleanFilter
-)
+from django_filters import BooleanFilter, DateTimeFromToRangeFilter
+from django_property_filter import PropertyFilterSet, PropertyBaseInFilter, PropertyCharFilter
 
 from mathesar.models import Schema, Table, Database
 
@@ -21,12 +19,9 @@ class Meta:
 
 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')
+    created = DateTimeFromToRangeFilter(field_name='created_at')
+    updated = DateTimeFromToRangeFilter(field_name='updated_at')
+    not_imported = BooleanFilter(lookup_expr="isnull", field_name='import_verified')
 
     class Meta:
         model = Table
@@ -34,8 +29,6 @@ class Meta:
 
 
 class DatabaseFilter(PropertyFilterSet):
-    deleted = PropertyBooleanFilter(field_name='deleted')
-
     class Meta:
         model = Database
         fields = ['deleted']
diff --git a/mathesar/serializers.py b/mathesar/serializers.py
--- a/mathesar/serializers.py
+++ b/mathesar/serializers.py
@@ -47,13 +47,12 @@ def get_url(self, obj):
 
 
 class SchemaSerializer(serializers.HyperlinkedModelSerializer):
-    tables = NestedTableSerializer(many=True, read_only=True)
     name = serializers.CharField()
     database = ModelNameField(max_length=128)
 
     class Meta:
         model = Schema
-        fields = ['id', 'name', 'tables', 'database', 'has_dependencies']
+        fields = ['id', 'name', 'database', 'has_dependencies']
 
 
 class TypeOptionSerializer(serializers.Serializer):
diff --git a/mathesar/views/frontend.py b/mathesar/views/frontend.py
--- a/mathesar/views/frontend.py
+++ b/mathesar/views/frontend.py
@@ -1,7 +1,7 @@
 from django.shortcuts import render, redirect, get_object_or_404
 
-from mathesar.models import Database, Schema
-from mathesar.serializers import DatabaseSerializer, SchemaSerializer
+from mathesar.models import Database, Schema, Table
+from mathesar.serializers import DatabaseSerializer, SchemaSerializer, TableSerializer
 
 
 def get_schema_list(request, database):
@@ -22,12 +22,24 @@ def get_database_list(request):
     return database_serializer.data
 
 
+def get_table_list(request, schema):
+    if schema is None:
+        return []
+    tables_serializer = TableSerializer(
+        Table.objects.filter(schema=schema),
+        many=True,
+        context={'request': request}
+    )
+    return tables_serializer.data
+
+
 def get_common_data(request, database, schema=None):
     return {
         'current_db': database.name if database else None,
         'current_schema': schema.id if schema else None,
         'schemas': get_schema_list(request, database),
-        'databases': get_database_list(request)
+        'databases': get_database_list(request),
+        'tables': get_table_list(request, schema)
     }
 
 


Test patch:
diff --git a/mathesar/tests/views/api/conftest.py b/mathesar/tests/views/api/conftest.py
--- a/mathesar/tests/views/api/conftest.py
+++ b/mathesar/tests/views/api/conftest.py
@@ -91,7 +91,7 @@ def empty_nasa_table(patent_schema):
     )
     db_table.create()
     db_table_oid = get_oid_from_table(db_table.name, db_table.schema, engine)
-    table = Table.objects.create(oid=db_table_oid, schema=patent_schema)
+    table = Table.current_objects.create(oid=db_table_oid, schema=patent_schema)
     return table
 
 
diff --git a/mathesar/tests/views/api/test_schema_api.py b/mathesar/tests/views/api/test_schema_api.py
--- a/mathesar/tests/views/api/test_schema_api.py
+++ b/mathesar/tests/views/api/test_schema_api.py
@@ -9,20 +9,11 @@
 from mathesar.utils.schemas import create_schema_and_object
 
 
-def check_schema_response(response_schema, schema, schema_name, test_db_name,
-                          len_tables=1, check_schema_objects=True):
+def check_schema_response(response_schema, schema, schema_name, test_db_name, check_schema_objects=True):
     assert response_schema['id'] == schema.id
     assert response_schema['name'] == schema_name
     assert response_schema['database'] == test_db_name
     assert 'has_dependencies' in response_schema
-    assert len(response_schema['tables']) == len_tables
-    if len_tables > 0:
-        response_table = response_schema['tables'][0]
-        assert 'id' in response_table
-        response_table_id = response_table['id']
-        assert 'name' in response_table
-        assert response_table['url'].startswith('http')
-        assert response_table['url'].endswith(f'/api/v0/tables/{response_table_id}/')
     if check_schema_objects:
         assert schema_name in schemas.get_mathesar_schemas(
             create_mathesar_engine(test_db_name)
@@ -91,8 +82,7 @@ def mock_get_name_from_oid(oid, engine):
             schema = schemas[query_tuple]
             response_schema = response_schemas[query_tuple]
             check_schema_response(response_schema, schema, schema.name,
-                                  schema.database.name, len_tables=0,
-                                  check_schema_objects=False)
+                                  schema.database.name, check_schema_objects=False)
 
 
 def test_schema_detail(create_table, client, test_db_name):
@@ -145,8 +135,7 @@ def test_schema_partial_update(create_schema, client, test_db_name):
 
     response_schema = response.json()
     assert response.status_code == 200
-    check_schema_response(response_schema, schema, new_schema_name, test_db_name,
-                          len_tables=0)
+    check_schema_response(response_schema, schema, new_schema_name, test_db_name,)
 
     schema = Schema.objects.get(oid=schema.oid)
     assert schema.name == new_schema_name
@@ -165,8 +154,7 @@ def test_schema_patch_same_name(create_schema, client, test_db_name):
         response_schema,
         schema,
         schema_name,
-        test_db_name,
-        len_tables=0
+        test_db_name
     )
     schema = Schema.objects.get(oid=schema.oid)
     assert schema.name == schema_name
diff --git a/mathesar/tests/views/api/test_table_api.py b/mathesar/tests/views/api/test_table_api.py
--- a/mathesar/tests/views/api/test_table_api.py
+++ b/mathesar/tests/views/api/test_table_api.py
@@ -6,7 +6,7 @@
 from sqlalchemy import text
 
 from mathesar import reflection
-from mathesar.models import Table, DataFile, Schema
+from mathesar.models import Table, DataFile
 from db.tests.types import fixtures
 from db import tables
 
@@ -216,19 +216,19 @@ def test_table_list_filter_schema(create_table, client):
         'Schema 3': create_table('Filter Schema 3', schema='Schema 3')
     }
 
-    filter_tables = ['Schema 2', 'Schema 3']
-    query_str = ','.join(filter_tables)
-    response = client.get(f'/api/v0/tables/?schema={query_str}')
+    schema_name = 'Schema 1'
+    schema_id = expected_tables[schema_name].schema.id
+    response = client.get(f'/api/v0/tables/?schema={schema_id}')
     response_data = response.json()
-    check_table_filter_response(response, status_code=200, count=2)
+    check_table_filter_response(response, status_code=200, count=1)
 
-    response_tables = {Schema.objects.get(id=res['schema']).name: res
+    response_tables = {res['schema']: res
                        for res in response_data['results']}
-    for schema_name in filter_tables:
-        assert schema_name in response_tables
-        table = expected_tables[schema_name]
-        response_table = response_tables[schema_name]
-        check_table_response(response_table, table, table.name)
+
+    assert schema_id in response_tables
+    table = expected_tables[schema_name]
+    response_table = response_tables[schema_id]
+    check_table_response(response_table, table, table.name)
 
 
 @pytest.mark.parametrize('timestamp_type', ['created', 'updated'])