diff --git a/superset/commands/dataset/export.py b/superset/commands/dataset/export.py index 5422f821577..e298a8f58f1 100644 --- a/superset/commands/dataset/export.py +++ b/superset/commands/dataset/export.py @@ -78,6 +78,19 @@ class ExportDatasetsCommand(ExportModelsCommand): payload["version"] = EXPORT_VERSION payload["database_uuid"] = str(model.database.uuid) + # Always set cache_timeout from the property to ensure correct value + payload["cache_timeout"] = model.cache_timeout + + # SQLAlchemy returns column names as quoted_name objects which PyYAML cannot + # serialize. Convert all keys to regular strings to fix YAML serialization. + try: + from sqlalchemy.sql.elements import quoted_name + + if any(isinstance(key, quoted_name) for key in payload.keys()): + payload = {str(key): value for key, value in payload.items()} + except ImportError: + pass + file_content = yaml.safe_dump(payload, sort_keys=False) return file_content diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 3c0f45feab5..03c2d35fd8e 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -225,9 +225,7 @@ class BaseDatasource( This allows each datasource to override caching, while falling back to database-level defaults when appropriate. """ - if self._cache_timeout is not None: - return self._cache_timeout - return self.database.cache_timeout + return self._cache_timeout or self.database.cache_timeout @cache_timeout.setter def cache_timeout(self, value: int | None) -> None: diff --git a/superset/utils/core.py b/superset/utils/core.py index bf0b97bbb47..b4058165b27 100644 --- a/superset/utils/core.py +++ b/superset/utils/core.py @@ -1678,7 +1678,7 @@ def get_metric_type_from_column( from superset.connectors.sqla.models import SqlMetric # Explorable datasources may not have metrics attribute - if not hasattr(datasource, "metrics"): + if datasource is None or not hasattr(datasource, "metrics"): return "" metric: SqlMetric = next( diff --git a/tests/integration_tests/dict_import_export_tests.py b/tests/integration_tests/dict_import_export_tests.py index b0855123305..96265ccb2b7 100644 --- a/tests/integration_tests/dict_import_export_tests.py +++ b/tests/integration_tests/dict_import_export_tests.py @@ -65,9 +65,9 @@ class TestDictImportExport(SupersetTestCase): cols_uuids=None, metric_names=[], # noqa: B006 ): - database_name = "main" + database = get_example_database() name = f"{NAME_PREFIX}{name}" - params = {DBREF: id, "database_name": database_name} + params = {DBREF: id, "database_name": database.database_name} if cols_uuids is None: cols_uuids = [None] * len(cols_names) @@ -86,7 +86,11 @@ class TestDictImportExport(SupersetTestCase): } table = SqlaTable( - id=id, schema=schema, table_name=name, params=json.dumps(params) + id=id, + schema=schema, + table_name=name, + params=json.dumps(params), + database=database, ) for col_name, uuid in zip(cols_names, cols_uuids, strict=False): table.columns.append(TableColumn(column_name=col_name, uuid=uuid)) diff --git a/tests/unit_tests/utils/map_type_tests.py b/tests/unit_tests/utils/map_type_tests.py index b076880e147..a91fe1abf0a 100644 --- a/tests/unit_tests/utils/map_type_tests.py +++ b/tests/unit_tests/utils/map_type_tests.py @@ -16,8 +16,6 @@ # from unittest.mock import MagicMock, patch -import pytest - from superset.connectors.sqla.models import SqlMetric from superset.utils.core import ( get_metric_type_from_column, @@ -62,8 +60,7 @@ def test_column_is_none(): def test_datasource_is_none(): datasource = None column = "my_column" - with pytest.raises(AttributeError): - get_metric_type_from_column(column, datasource) + assert get_metric_type_from_column(column, datasource) == "" def test_none_input():