You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/03/20 19:43:18 UTC

(superset) 17/32: fix(chart): prevent chart list from failing when a datasource lacks explore_url (#38721)

This is an automated email from the ASF dual-hosted git repository.

michaelsmolina pushed a commit to branch 6.1
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 25915f016b64f679b64364eff8d5aa25f6a943af
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Wed Mar 18 10:23:57 2026 -0300

    fix(chart): prevent chart list from failing when a datasource lacks explore_url (#38721)
---
 superset/models/slice.py              |  7 +++++--
 tests/unit_tests/models/slice_test.py | 38 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/superset/models/slice.py b/superset/models/slice.py
index 8b864f4ddc2..04c698ce95d 100644
--- a/superset/models/slice.py
+++ b/superset/models/slice.py
@@ -164,10 +164,13 @@ class Slice(  # pylint: disable=too-many-public-methods
 
     @renders("datasource_url")
     def datasource_url(self) -> str | None:
+        # Use getattr to guard against datasource types that don't have explore_url
+        # (e.g. Query objects), which would otherwise raise AttributeError and cause
+        # the entire chart list response to fail.
         if self.table:
-            return self.table.explore_url
+            return getattr(self.table, "explore_url", None)
         datasource = self.datasource
-        return datasource.explore_url if datasource else None
+        return getattr(datasource, "explore_url", None) if datasource else None
 
     def datasource_name_text(self) -> str | None:
         if self.table:
diff --git a/tests/unit_tests/models/slice_test.py b/tests/unit_tests/models/slice_test.py
index dc6bbb86552..e55d00dede0 100644
--- a/tests/unit_tests/models/slice_test.py
+++ b/tests/unit_tests/models/slice_test.py
@@ -85,3 +85,41 @@ class TestSlice:
         """Test id_or_uuid_filter returns correct BinaryExpression."""
         result = id_or_uuid_filter(input_value)
         assert result is not None
+
+    def test_datasource_url_returns_none_when_datasource_lacks_explore_url(self):
+        """datasource_url() must not raise when the datasource has no explore_url.
+
+        Charts whose datasource resolves to a Query (or any other type without
+        explore_url) used to raise AttributeError, which caused the entire chart
+        list API response to fail instead of just skipping that one chart.
+        """
+        slc = Slice()
+        slc.id = 1
+
+        # Simulate a datasource object that does NOT have explore_url (e.g. Query)
+        mock_datasource = MagicMock(spec=[])  # spec=[] means no attributes at all
+        slc.table = mock_datasource
+
+        result = slc.datasource_url()
+        assert result is None
+
+    def test_datasource_url_returns_explore_url_when_present(self):
+        """datasource_url() returns the datasource explore_url when it exists."""
+        slc = Slice()
+        slc.id = 1
+
+        mock_table = MagicMock()
+        mock_table.explore_url = "/explore/?datasource_type=table&datasource_id=1"
+        slc.table = mock_table
+
+        result = slc.datasource_url()
+        assert result == "/explore/?datasource_type=table&datasource_id=1"
+
+    def test_datasource_url_returns_none_when_no_datasource(self):
+        """datasource_url() returns None when there is no datasource."""
+        slc = Slice()
+        slc.id = 1
+        slc.table = None
+
+        result = slc.datasource_url()
+        assert result is None