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/04/30 11:52:04 UTC

(superset) branch 5.0-pulse updated: fix(drill-to-detail): drill to detail by correctly filtering by metric (#39766)

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

michael-s-molina pushed a commit to branch 5.0-pulse
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/5.0-pulse by this push:
     new 5c3fe6f49bc fix(drill-to-detail): drill to detail by correctly filtering by metric (#39766)
5c3fe6f49bc is described below

commit 5c3fe6f49bce4ffc334feb74e3ea10dd8fcd67e4
Author: Luiz Otavio <45...@users.noreply.github.com>
AuthorDate: Thu Apr 30 08:40:16 2026 -0300

    fix(drill-to-detail): drill to detail by correctly filtering by metric (#39766)
    
    Co-authored-by: Michael S. Molina <mi...@gmail.com>
    (cherry picked from commit df396aa6e9d6614c33d432c072ae1920475d33bc)
---
 superset/models/helpers.py              | 19 ++++++++++++++
 tests/unit_tests/models/helpers_test.py | 46 +++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+)

diff --git a/superset/models/helpers.py b/superset/models/helpers.py
index 7604f604f1f..be82c266362 100644
--- a/superset/models/helpers.py
+++ b/superset/models/helpers.py
@@ -1772,6 +1772,25 @@ class ExploreMixin:  # pylint: disable=too-many-public-methods
                     continue
             else:
                 col_obj = columns_by_name.get(cast(str, flt_col))
+                # If not found in columns, check if it's a metric
+                # This supports filtering on metric columns for any chart type
+                if col_obj is None:
+                    # Fall back to verbose_name so filters produced by
+                    # right-click "Drill to detail by" (which can pass a
+                    # column's verbose label) still resolve to a real column.
+                    col_obj = next(
+                        (c for c in self.columns if c.verbose_name == flt_col),
+                        None,
+                    )
+                if (
+                    col_obj is None
+                    and isinstance(flt_col, str)
+                    and flt_col in metrics_by_name
+                ):
+                    # Convert metric to SQLA column expression
+                    sqla_col = metrics_by_name[flt_col].get_sqla_col(
+                        template_processor=template_processor
+                    )
             filter_grain = flt.get("grain")
 
             if get_column_name(flt_col) in removed_filters:
diff --git a/tests/unit_tests/models/helpers_test.py b/tests/unit_tests/models/helpers_test.py
index 4e8aa493be3..2aa450cb736 100644
--- a/tests/unit_tests/models/helpers_test.py
+++ b/tests/unit_tests/models/helpers_test.py
@@ -210,3 +210,49 @@ def test_values_for_column_double_percents(
 
         assert called_sql.compare(expected_sql) is True
         assert called_conn == engine
+
+
+def test_filter_by_verbose_name_resolves_to_column(
+    database: Database,
+) -> None:
+    """
+    A filter whose "col" value matches a column's verbose_name
+    (e.g. the label emitted by "Drill to detail by") must resolve to that
+    column and produce a WHERE clause on the underlying column_name.
+    """
+    from superset.connectors.sqla.models import SqlaTable, TableColumn
+
+    table = SqlaTable(
+        database=database,
+        schema=None,
+        table_name="t",
+        columns=[
+            TableColumn(
+                column_name="country_code",
+                verbose_name="Country",
+                type="TEXT",
+            ),
+            TableColumn(column_name="b", type="TEXT"),
+        ],
+    )
+
+    sqla_query = table.get_sqla_query(
+        columns=["b"],
+        # Filter uses the verbose label, as "Drill to detail by" does.
+        filter=[{"col": "Country", "op": "==", "val": "US"}],
+        is_timeseries=False,
+        row_limit=10,
+    )
+
+    with database.get_sqla_engine() as engine:
+        sql = str(
+            sqla_query.sqla_query.compile(
+                dialect=engine.dialect,
+                compile_kwargs={"literal_binds": True},
+            )
+        )
+
+    # The filter should be translated to a WHERE clause on the real column.
+    assert "WHERE" in sql, f"Expected WHERE clause, got SQL: {sql}"
+    assert "country_code" in sql, f"Expected filter on 'country_code', got SQL: {sql}"
+    assert "'US'" in sql, f"Expected filter value 'US', got SQL: {sql}"