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 2024/03/21 18:51:16 UTC

(superset) branch master updated: feat: Adds option to disable drill to detail per database (#27536)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 6e528426dd feat: Adds option to disable drill to detail per database (#27536)
6e528426dd is described below

commit 6e528426ddc3f03159226156e222769d988a215b
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Thu Mar 21 15:51:09 2024 -0300

    feat: Adds option to disable drill to detail per database (#27536)
---
 docs/static/resources/openapi.json                 |   3 +
 .../DrillDetail/DrillDetailMenuItems.test.tsx      |  16 +-
 .../Chart/DrillDetail/DrillDetailMenuItems.tsx     | 185 ++++++++++-----------
 .../DatabaseSelector/DatabaseSelector.test.tsx     |   4 +
 superset-frontend/src/dashboard/types.ts           |   2 +
 .../databases/DatabaseModal/ExtraOptions.tsx       |  16 ++
 superset-frontend/src/features/databases/types.ts  |   1 +
 .../AddDataset/LeftPanel/LeftPanel.test.tsx        |   4 +
 superset-frontend/src/types/Database.ts            |   1 +
 superset/dashboards/schemas.py                     |   1 +
 superset/databases/api.py                          |   1 +
 superset/databases/schemas.py                      |   5 +-
 superset/models/core.py                            |   8 +-
 superset/sqllab/utils.py                           |   1 +
 superset/views/database/mixins.py                  |   4 +-
 tests/integration_tests/core_tests.py              |  23 +++
 tests/integration_tests/databases/api_tests.py     |   1 +
 17 files changed, 170 insertions(+), 106 deletions(-)

diff --git a/docs/static/resources/openapi.json b/docs/static/resources/openapi.json
index 13d5c8c21c..7c941662ad 100644
--- a/docs/static/resources/openapi.json
+++ b/docs/static/resources/openapi.json
@@ -3613,6 +3613,9 @@
           "disable_data_preview": {
             "type": "boolean"
           },
+          "disable_drill_to_detail": {
+            "type": "boolean"
+          },
           "explore_database_id": {
             "type": "integer"
           },
diff --git a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx
index 580bf4c522..73cc4a2868 100644
--- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx
+++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.test.tsx
@@ -285,11 +285,11 @@ test('context menu for supported chart, no dimensions, no filters', async () =>
     isContextMenu: true,
   });
 
-  await expectDrillToDetailDisabled(
-    'Drill to detail is disabled because this chart does not group data by dimension value.',
-  );
+  const message =
+    'Drill to detail is disabled because this chart does not group data by dimension value.';
 
-  await expectDrillToDetailByDisabled();
+  await expectDrillToDetailDisabled(message);
+  await expectDrillToDetailByDisabled(message);
 });
 
 test('context menu for supported chart, no dimensions, 1 filter', async () => {
@@ -299,11 +299,11 @@ test('context menu for supported chart, no dimensions, 1 filter', async () => {
     filters: [filterA],
   });
 
-  await expectDrillToDetailDisabled(
-    'Drill to detail is disabled because this chart does not group data by dimension value.',
-  );
+  const message =
+    'Drill to detail is disabled because this chart does not group data by dimension value.';
 
-  await expectDrillToDetailByDisabled();
+  await expectDrillToDetailDisabled(message);
+  await expectDrillToDetailByDisabled(message);
 });
 
 test('dropdown menu for supported chart, dimensions', async () => {
diff --git a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
index 6ef9b331e6..6c1669933b 100644
--- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
+++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
@@ -30,13 +30,30 @@ import {
   styled,
   t,
 } from '@superset-ui/core';
+import { useSelector } from 'react-redux';
 import { Menu } from 'src/components/Menu';
+import { RootState } from 'src/dashboard/types';
 import DrillDetailModal from './DrillDetailModal';
 import { getSubmenuYOffset } from '../utils';
 import { MenuItemTooltip } from '../DisabledMenuItemTooltip';
 import { MenuItemWithTruncation } from '../MenuItemWithTruncation';
 
-const DRILL_TO_DETAIL_TEXT = t('Drill to detail by');
+const DRILL_TO_DETAIL = t('Drill to detail');
+const DRILL_TO_DETAIL_BY = t('Drill to detail by');
+const DISABLED_REASONS = {
+  DATABASE: t(
+    'Drill to detail is disabled for this database. Change the database settings to enable it.',
+  ),
+  NO_AGGREGATIONS: t(
+    'Drill to detail is disabled because this chart does not group data by dimension value.',
+  ),
+  NO_FILTERS: t(
+    'Right-click on a dimension value to drill to detail by that value.',
+  ),
+  NOT_SUPPORTED: t(
+    'Drill to detail by value is not yet supported for this chart type.',
+  ),
+};
 
 const DisabledMenuItem = ({ children, ...props }: { children: ReactNode }) => (
   <Menu.Item disabled {...props}>
@@ -94,6 +111,11 @@ const DrillDetailMenuItems = ({
   submenuIndex = 0,
   ...props
 }: DrillDetailMenuItemsProps) => {
+  const drillToDetailDisabled = useSelector<RootState, boolean | undefined>(
+    ({ datasources }) =>
+      datasources[formData.datasource]?.database?.disable_drill_to_detail,
+  );
+
   const [modalFilters, setFilters] = useState<BinaryQueryObjectFilterClause[]>(
     [],
   );
@@ -132,52 +154,6 @@ const DrillDetailMenuItems = ({
     return isEmpty(metrics);
   }, [formData]);
 
-  let drillToDetailMenuItem;
-  if (handlesDimensionContextMenu && noAggregations) {
-    drillToDetailMenuItem = (
-      <DisabledMenuItem {...props} key="drill-detail-no-aggregations">
-        {t('Drill to detail')}
-        <MenuItemTooltip
-          title={t(
-            'Drill to detail is disabled because this chart does not group data by dimension value.',
-          )}
-        />
-      </DisabledMenuItem>
-    );
-  } else {
-    drillToDetailMenuItem = (
-      <Menu.Item
-        {...props}
-        key="drill-detail-no-filters"
-        onClick={openModal.bind(null, [])}
-      >
-        {t('Drill to detail')}
-      </Menu.Item>
-    );
-  }
-
-  let drillToDetailByMenuItem;
-  if (!handlesDimensionContextMenu) {
-    drillToDetailByMenuItem = (
-      <DisabledMenuItem {...props} key="drill-detail-by-chart-not-supported">
-        {DRILL_TO_DETAIL_TEXT}
-        <MenuItemTooltip
-          title={t(
-            'Drill to detail by value is not yet supported for this chart type.',
-          )}
-        />
-      </DisabledMenuItem>
-    );
-  }
-
-  if (handlesDimensionContextMenu && noAggregations) {
-    drillToDetailByMenuItem = (
-      <DisabledMenuItem {...props} key="drill-detail-by-no-aggregations">
-        {DRILL_TO_DETAIL_TEXT}
-      </DisabledMenuItem>
-    );
-  }
-
   // Ensure submenu doesn't appear offscreen
   const submenuYOffset = useMemo(
     () =>
@@ -189,55 +165,76 @@ const DrillDetailMenuItems = ({
     [contextMenuY, filters.length, submenuIndex],
   );
 
-  if (handlesDimensionContextMenu && !noAggregations && filters?.length) {
-    drillToDetailByMenuItem = (
-      <Menu.SubMenu
-        {...props}
-        popupOffset={[0, submenuYOffset]}
-        popupClassName="chart-context-submenu"
-        title={DRILL_TO_DETAIL_TEXT}
-      >
-        <div data-test="drill-to-detail-by-submenu">
-          {filters.map((filter, i) => (
-            <MenuItemWithTruncation
-              {...props}
-              tooltipText={`${DRILL_TO_DETAIL_TEXT} ${filter.formattedVal}`}
-              key={`drill-detail-filter-${i}`}
-              onClick={openModal.bind(null, [filter])}
-            >
-              {`${DRILL_TO_DETAIL_TEXT} `}
-              <StyledFilter stripHTML>{filter.formattedVal}</StyledFilter>
-            </MenuItemWithTruncation>
-          ))}
-          {filters.length > 1 && (
-            <Menu.Item
-              {...props}
-              key="drill-detail-filter-all"
-              onClick={openModal.bind(null, filters)}
-            >
-              <div>
-                {`${DRILL_TO_DETAIL_TEXT} `}
-                <StyledFilter stripHTML={false}>{t('all')}</StyledFilter>
-              </div>
-            </Menu.Item>
-          )}
-        </div>
-      </Menu.SubMenu>
-    );
+  let drillDisabled;
+  let drillByDisabled;
+  if (drillToDetailDisabled) {
+    drillDisabled = DISABLED_REASONS.DATABASE;
+    drillByDisabled = DISABLED_REASONS.DATABASE;
+  } else if (handlesDimensionContextMenu) {
+    if (noAggregations) {
+      drillDisabled = DISABLED_REASONS.NO_AGGREGATIONS;
+      drillByDisabled = DISABLED_REASONS.NO_AGGREGATIONS;
+    } else if (!filters?.length) {
+      drillByDisabled = DISABLED_REASONS.NO_FILTERS;
+    }
+  } else {
+    drillByDisabled = DISABLED_REASONS.NOT_SUPPORTED;
   }
 
-  if (handlesDimensionContextMenu && !noAggregations && !filters?.length) {
-    drillToDetailByMenuItem = (
-      <DisabledMenuItem {...props} key="drill-detail-by-select-aggregation">
-        {DRILL_TO_DETAIL_TEXT}
-        <MenuItemTooltip
-          title={t(
-            'Right-click on a dimension value to drill to detail by that value.',
-          )}
-        />
-      </DisabledMenuItem>
-    );
-  }
+  const drillToDetailMenuItem = drillDisabled ? (
+    <DisabledMenuItem {...props} key="drill-to-detail-disabled">
+      {DRILL_TO_DETAIL}
+      <MenuItemTooltip title={drillDisabled} />
+    </DisabledMenuItem>
+  ) : (
+    <Menu.Item
+      {...props}
+      key="drill-to-detail"
+      onClick={openModal.bind(null, [])}
+    >
+      {DRILL_TO_DETAIL}
+    </Menu.Item>
+  );
+
+  const drillToDetailByMenuItem = drillByDisabled ? (
+    <DisabledMenuItem {...props} key="drill-to-detail-by-disabled">
+      {DRILL_TO_DETAIL_BY}
+      <MenuItemTooltip title={drillByDisabled} />
+    </DisabledMenuItem>
+  ) : (
+    <Menu.SubMenu
+      {...props}
+      popupOffset={[0, submenuYOffset]}
+      popupClassName="chart-context-submenu"
+      title={DRILL_TO_DETAIL_BY}
+    >
+      <div data-test="drill-to-detail-by-submenu">
+        {filters.map((filter, i) => (
+          <MenuItemWithTruncation
+            {...props}
+            tooltipText={`${DRILL_TO_DETAIL_BY} ${filter.formattedVal}`}
+            key={`drill-detail-filter-${i}`}
+            onClick={openModal.bind(null, [filter])}
+          >
+            {`${DRILL_TO_DETAIL_BY} `}
+            <StyledFilter stripHTML>{filter.formattedVal}</StyledFilter>
+          </MenuItemWithTruncation>
+        ))}
+        {filters.length > 1 && (
+          <Menu.Item
+            {...props}
+            key="drill-detail-filter-all"
+            onClick={openModal.bind(null, filters)}
+          >
+            <div>
+              {`${DRILL_TO_DETAIL_BY} `}
+              <StyledFilter stripHTML={false}>{t('all')}</StyledFilter>
+            </div>
+          </Menu.Item>
+        )}
+      </div>
+    </Menu.SubMenu>
+  );
 
   return (
     <>
diff --git a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
index 18e6769912..c3ad51cf60 100644
--- a/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/DatabaseSelector.test.tsx
@@ -62,6 +62,7 @@ const fakeDatabaseApiResult = {
     allows_subquery: 'Allows Subquery',
     allows_virtual_table_explore: 'Allows Virtual Table Explore',
     disable_data_preview: 'Disables SQL Lab Data Preview',
+    disable_drill_to_detail: 'Disable Drill To Detail',
     backend: 'Backend',
     changed_on: 'Changed On',
     changed_on_delta_humanized: 'Changed On Delta Humanized',
@@ -83,6 +84,7 @@ const fakeDatabaseApiResult = {
     'allows_subquery',
     'allows_virtual_table_explore',
     'disable_data_preview',
+    'disable_drill_to_detail',
     'backend',
     'changed_on',
     'changed_on_delta_humanized',
@@ -116,6 +118,7 @@ const fakeDatabaseApiResult = {
       allows_subquery: true,
       allows_virtual_table_explore: true,
       disable_data_preview: false,
+      disable_drill_to_detail: false,
       backend: 'postgresql',
       changed_on: '2021-03-09T19:02:07.141095',
       changed_on_delta_humanized: 'a day ago',
@@ -136,6 +139,7 @@ const fakeDatabaseApiResult = {
       allows_subquery: true,
       allows_virtual_table_explore: true,
       disable_data_preview: false,
+      disable_drill_to_detail: false,
       backend: 'mysql',
       changed_on: '2021-03-09T19:02:07.141095',
       changed_on_delta_humanized: 'a day ago',
diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts
index bd70398eca..5aa8020b4d 100644
--- a/superset-frontend/src/dashboard/types.ts
+++ b/superset-frontend/src/dashboard/types.ts
@@ -29,6 +29,7 @@ import {
 import { Dataset } from '@superset-ui/chart-controls';
 import { chart } from 'src/components/Chart/chartReducer';
 import componentTypes from 'src/dashboard/util/componentTypes';
+import Database from 'src/types/Database';
 import { UrlParamEntries } from 'src/utils/urlUtils';
 
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
@@ -143,6 +144,7 @@ export type Datasource = Dataset & {
   uid: string;
   column_types: GenericDataType[];
   table_name: string;
+  database?: Database;
 };
 export type DatasourcesState = {
   [key: string]: Datasource;
diff --git a/superset-frontend/src/features/databases/DatabaseModal/ExtraOptions.tsx b/superset-frontend/src/features/databases/DatabaseModal/ExtraOptions.tsx
index 45706da586..28e02a02f0 100644
--- a/superset-frontend/src/features/databases/DatabaseModal/ExtraOptions.tsx
+++ b/superset-frontend/src/features/databases/DatabaseModal/ExtraOptions.tsx
@@ -571,6 +571,22 @@ const ExtraOptions = ({
             )}
           </div>
         </StyledInputContainer>
+        <StyledInputContainer css={no_margin_bottom}>
+          <div className="input-container">
+            <IndeterminateCheckbox
+              id="disable_drill_to_detail"
+              indeterminate={false}
+              checked={!!extraJson?.disable_drill_to_detail}
+              onChange={onExtraInputChange}
+              labelText={t('Disable drill to detail')}
+            />
+            <InfoTooltip
+              tooltip={t(
+                'Disables the drill to detail feature for this database.',
+              )}
+            />
+          </div>
+        </StyledInputContainer>
       </Collapse.Panel>
     </Collapse>
   );
diff --git a/superset-frontend/src/features/databases/types.ts b/superset-frontend/src/features/databases/types.ts
index 58d533c7be..2dff61d5e8 100644
--- a/superset-frontend/src/features/databases/types.ts
+++ b/superset-frontend/src/features/databases/types.ts
@@ -222,6 +222,7 @@ export interface ExtraJson {
   cancel_query_on_windows_unload?: boolean; // in Performance
   cost_estimate_enabled?: boolean; // in SQL Lab
   disable_data_preview?: boolean; // in SQL Lab
+  disable_drill_to_detail?: boolean;
   engine_params?: {
     catalog?: Record<string, string>;
     connect_args?: {
diff --git a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
index 5156073281..36eee842a9 100644
--- a/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/LeftPanel/LeftPanel.test.tsx
@@ -43,6 +43,7 @@ beforeEach(() => {
       allows_subquery: 'Allows Subquery',
       allows_virtual_table_explore: 'Allows Virtual Table Explore',
       disable_data_preview: 'Disables SQL Lab Data Preview',
+      disable_drill_to_detail: 'Disable Drill To Detail',
       backend: 'Backend',
       changed_on: 'Changed On',
       changed_on_delta_humanized: 'Changed On Delta Humanized',
@@ -65,6 +66,7 @@ beforeEach(() => {
       'allows_subquery',
       'allows_virtual_table_explore',
       'disable_data_preview',
+      'disable_drill_to_detail',
       'backend',
       'changed_on',
       'changed_on_delta_humanized',
@@ -99,6 +101,7 @@ beforeEach(() => {
         allows_subquery: true,
         allows_virtual_table_explore: true,
         disable_data_preview: false,
+        disable_drill_to_detail: false,
         backend: 'postgresql',
         changed_on: '2021-03-09T19:02:07.141095',
         changed_on_delta_humanized: 'a day ago',
@@ -120,6 +123,7 @@ beforeEach(() => {
         allows_subquery: true,
         allows_virtual_table_explore: true,
         disable_data_preview: false,
+        disable_drill_to_detail: false,
         backend: 'mysql',
         changed_on: '2021-03-09T19:02:07.141095',
         changed_on_delta_humanized: 'a day ago',
diff --git a/superset-frontend/src/types/Database.ts b/superset-frontend/src/types/Database.ts
index 575d69e2f2..69e86e3a7b 100644
--- a/superset-frontend/src/types/Database.ts
+++ b/superset-frontend/src/types/Database.ts
@@ -28,4 +28,5 @@ export default interface Database {
   sqlalchemy_uri: string;
   catalog: object;
   parameters: any;
+  disable_drill_to_detail?: boolean;
 }
diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py
index a208bae0f9..83c533b86e 100644
--- a/superset/dashboards/schemas.py
+++ b/superset/dashboards/schemas.py
@@ -215,6 +215,7 @@ class DatabaseSchema(Schema):
     allows_cost_estimate = fields.Bool()
     allows_virtual_table_explore = fields.Bool()
     disable_data_preview = fields.Bool()
+    disable_drill_to_detail = fields.Bool()
     explore_database_id = fields.Int()
 
 
diff --git a/superset/databases/api.py b/superset/databases/api.py
index 1e44a52106..6f9cb2a921 100644
--- a/superset/databases/api.py
+++ b/superset/databases/api.py
@@ -176,6 +176,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
         "id",
         "uuid",
         "disable_data_preview",
+        "disable_drill_to_detail",
         "engine_information",
     ]
     add_columns = [
diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py
index 65005824d5..d4a2fc021f 100644
--- a/superset/databases/schemas.py
+++ b/superset/databases/schemas.py
@@ -132,7 +132,9 @@ extra_description = markdown(
     "5. The ``allows_virtual_table_explore`` field is a boolean specifying "
     "whether or not the Explore button in SQL Lab results is shown.<br/>"
     "6. The ``disable_data_preview`` field is a boolean specifying whether or not data "
-    "preview queries will be run when fetching table metadata in SQL Lab.",
+    "preview queries will be run when fetching table metadata in SQL Lab."
+    "7. The ``disable_drill_to_detail`` field is a boolean specifying whether or not"
+    "drill to detail is disabled for the database.",
     True,
 )
 get_export_ids_schema = {"type": "array", "items": {"type": "integer"}}
@@ -750,6 +752,7 @@ class ImportV1DatabaseExtraSchema(Schema):
     allows_virtual_table_explore = fields.Boolean(required=False)
     cancel_query_on_windows_unload = fields.Boolean(required=False)
     disable_data_preview = fields.Boolean(required=False)
+    disable_drill_to_detail = fields.Boolean(required=False)
     version = fields.String(required=False, allow_none=True)
 
 
diff --git a/superset/models/core.py b/superset/models/core.py
index 71a6e9d042..ee8297eb11 100755
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -91,7 +91,6 @@ DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
 
 
 class KeyValue(Model):  # pylint: disable=too-few-public-methods
-
     """Used for any type of key-value store"""
 
     __tablename__ = "keyvalue"
@@ -116,7 +115,6 @@ class ConfigurationMethod(StrEnum):
 class Database(
     Model, AuditMixinNullable, ImportExportMixin
 ):  # pylint: disable=too-many-public-methods
-
     """An ORM object that stores Database related information"""
 
     __tablename__ = "dbs"
@@ -229,6 +227,11 @@ class Database(
         # this will prevent any 'trash value' strings from going through
         return self.get_extra().get("disable_data_preview", False) is True
 
+    @property
+    def disable_drill_to_detail(self) -> bool:
+        # this will prevent any 'trash value' strings from going through
+        return self.get_extra().get("disable_drill_to_detail", False) is True
+
     @property
     def schema_options(self) -> dict[str, Any]:
         """Additional schema display config for engines with complex schemas"""
@@ -248,6 +251,7 @@ class Database(
             "schema_options": self.schema_options,
             "parameters": self.parameters,
             "disable_data_preview": self.disable_data_preview,
+            "disable_drill_to_detail": self.disable_drill_to_detail,
             "parameters_schema": self.parameters_schema,
             "engine_information": self.engine_information,
         }
diff --git a/superset/sqllab/utils.py b/superset/sqllab/utils.py
index ca331d1e34..bbf3919640 100644
--- a/superset/sqllab/utils.py
+++ b/superset/sqllab/utils.py
@@ -38,6 +38,7 @@ DATABASE_KEYS = [
     "force_ctas_schema",
     "id",
     "disable_data_preview",
+    "disable_drill_to_detail",
 ]
 
 
diff --git a/superset/views/database/mixins.py b/superset/views/database/mixins.py
index dc48950353..c8fdaae500 100644
--- a/superset/views/database/mixins.py
+++ b/superset/views/database/mixins.py
@@ -147,7 +147,9 @@ class DatabaseMixin:
             "whether or not the Explore button in SQL Lab results is shown<br/>"
             "6. The ``disable_data_preview`` field is a boolean specifying whether or"
             "not data preview queries will be run when fetching table metadata in"
-            "SQL Lab.",
+            "SQL Lab."
+            "7. The ``disable_drill_to_detail`` field is a boolean specifying whether or"
+            "not drill to detail is disabled for the database.",
             True,
         ),
         "encrypted_extra": utils.markdown(
diff --git a/tests/integration_tests/core_tests.py b/tests/integration_tests/core_tests.py
index ceb2bf7782..0437e3a92a 100644
--- a/tests/integration_tests/core_tests.py
+++ b/tests/integration_tests/core_tests.py
@@ -1073,6 +1073,29 @@ class TestCore(SupersetTestCase):
         database.extra = json.dumps(extra)
         self.assertEqual(database.disable_data_preview, False)
 
+    def test_disable_drill_to_detail(self):
+        # test that disable_drill_to_detail is False by default
+        database = utils.get_example_database()
+        self.assertEqual(database.disable_drill_to_detail, False)
+
+        # test that disable_drill_to_detail can be set to True
+        extra = database.get_extra()
+        extra["disable_drill_to_detail"] = True
+        database.extra = json.dumps(extra)
+        self.assertEqual(database.disable_drill_to_detail, True)
+
+        # test that disable_drill_to_detail can be set to False
+        extra = database.get_extra()
+        extra["disable_drill_to_detail"] = False
+        database.extra = json.dumps(extra)
+        self.assertEqual(database.disable_drill_to_detail, False)
+
+        # test that disable_drill_to_detail is not broken with bad values
+        extra = database.get_extra()
+        extra["disable_drill_to_detail"] = "trash value"
+        database.extra = json.dumps(extra)
+        self.assertEqual(database.disable_drill_to_detail, False)
+
     def test_explore_database_id(self):
         database = superset.utils.database.get_example_database()
         explore_database = superset.utils.database.get_example_database()
diff --git a/tests/integration_tests/databases/api_tests.py b/tests/integration_tests/databases/api_tests.py
index 0f9dc03723..249b953960 100644
--- a/tests/integration_tests/databases/api_tests.py
+++ b/tests/integration_tests/databases/api_tests.py
@@ -204,6 +204,7 @@ class TestDatabaseApi(SupersetTestCase):
             "created_by",
             "database_name",
             "disable_data_preview",
+            "disable_drill_to_detail",
             "engine_information",
             "explore_database_id",
             "expose_in_sqllab",