mirror of https://github.com/apache/superset.git
[filtering] define combo of slice/fields unafected by filtering (#1179)
* [FilterBox] dashboard date range filtering * [filtering] define combo of slice/fields unafected by filtering * adding an entry to the docs * Addressed comments
This commit is contained in:
parent
7115c5458d
commit
aed473d0d2
|
@ -82,6 +82,32 @@ function dashboardContainer(dashboardData) {
|
|||
setFilter(sliceId, col, vals, refresh) {
|
||||
this.addFilter(sliceId, col, vals, false, refresh);
|
||||
},
|
||||
effectiveExtraFilters(sliceId) {
|
||||
// Summarized filter, not defined by sliceId
|
||||
// returns k=field, v=array of values
|
||||
const f = {};
|
||||
if (sliceId && this.metadata.filter_immune_slices.includes(sliceId)) {
|
||||
// The slice is immune to dashboard fiterls
|
||||
return f;
|
||||
}
|
||||
|
||||
// Building a list of fields the slice is immune to filters on
|
||||
let immuneToFields = [];
|
||||
if (
|
||||
sliceId &&
|
||||
this.metadata.filter_immune_slice_fields &&
|
||||
this.metadata.filter_immune_slice_fields[sliceId]) {
|
||||
immuneToFields = this.metadata.filter_immune_slice_fields[sliceId];
|
||||
}
|
||||
for (const filteringSliceId in this.filters) {
|
||||
for (const field in this.filters[filteringSliceId]) {
|
||||
if (!immuneToFields.includes(field)) {
|
||||
f[field] = this.filters[filteringSliceId][field];
|
||||
}
|
||||
}
|
||||
}
|
||||
return f;
|
||||
},
|
||||
addFilter(sliceId, col, vals, merge = true, refresh = true) {
|
||||
if (!(sliceId in this.filters)) {
|
||||
this.filters[sliceId] = {};
|
||||
|
|
|
@ -81,9 +81,11 @@ const px = function () {
|
|||
const parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
if (dashboard !== undefined) {
|
||||
const flts =
|
||||
newParams.extraFilters === false ? '' :
|
||||
encodeURIComponent(JSON.stringify(dashboard.filters));
|
||||
let flts = '';
|
||||
if (newParams.extraFilters !== false) {
|
||||
flts = dashboard.effectiveExtraFilters(sliceId);
|
||||
flts = encodeURIComponent(JSON.stringify(flts));
|
||||
}
|
||||
qrystr = parser.search + '&extra_filters=' + flts;
|
||||
} else if ($('#query').length === 0) {
|
||||
qrystr = parser.search;
|
||||
|
|
|
@ -12,16 +12,15 @@ import './filter_box.css';
|
|||
import { TIME_CHOICES } from './constants.js';
|
||||
|
||||
const propTypes = {
|
||||
origSelectedValues: React.PropTypes.object,
|
||||
filtersChoices: React.PropTypes.object,
|
||||
onChange: React.PropTypes.func,
|
||||
origSelectedValues: React.PropTypes.object,
|
||||
showDateFilter: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
filtersChoices: {},
|
||||
onChange: () => {},
|
||||
origSelectedValues: {},
|
||||
onChange: () => {},
|
||||
showDateFilter: false,
|
||||
};
|
||||
|
||||
|
|
|
@ -445,6 +445,14 @@ def generic_find_constraint_name(table, columns, referenced, db):
|
|||
return fk.name
|
||||
|
||||
|
||||
def validate_json(obj):
|
||||
if obj:
|
||||
try:
|
||||
json.loads(obj)
|
||||
except Exception:
|
||||
raise CaravelException("JSON is not valid")
|
||||
|
||||
|
||||
class timeout(object):
|
||||
"""
|
||||
To be used in a ``with`` block and timeout its content.
|
||||
|
|
|
@ -850,6 +850,8 @@ class DashboardModelView(CaravelModelView, DeleteMixin): # noqa
|
|||
obj.slug = re.sub(r'\W+', '', obj.slug)
|
||||
if g.user not in obj.owners:
|
||||
obj.owners.append(g.user)
|
||||
utils.validate_json(obj.json_metadata)
|
||||
utils.validate_json(obj.position_json)
|
||||
|
||||
def pre_update(self, obj):
|
||||
check_ownership(obj)
|
||||
|
@ -1369,6 +1371,8 @@ class Caravel(BaseCaravelView):
|
|||
md = dash.metadata_dejson
|
||||
if 'filter_immune_slices' not in md:
|
||||
md['filter_immune_slices'] = []
|
||||
if 'filter_immune_slice_fields' not in md:
|
||||
md['filter_immune_slice_fields'] = {}
|
||||
md['expanded_slices'] = data['expanded_slices']
|
||||
dash.json_metadata = json.dumps(md, indent=4)
|
||||
dash.css = data['css']
|
||||
|
|
|
@ -196,12 +196,7 @@ class BaseViz(object):
|
|||
extra_filters = self.form_data.get('extra_filters')
|
||||
if not extra_filters:
|
||||
return {}
|
||||
extra_filters = json.loads(extra_filters)
|
||||
# removing per-slice details
|
||||
summary = {}
|
||||
for flt in extra_filters.values():
|
||||
summary.update(flt)
|
||||
return summary
|
||||
return json.loads(extra_filters)
|
||||
|
||||
def query_filters(self, is_having_filter=False):
|
||||
"""Processes the filters for the query"""
|
||||
|
|
48
docs/faq.rst
48
docs/faq.rst
|
@ -61,3 +61,51 @@ Why is the map not visible in the mapbox visualization?
|
|||
|
||||
You need to register to mapbox.com, get an API key and configure it as
|
||||
``MAPBOX_API_KEY`` in ``caravel_config.py``.
|
||||
|
||||
|
||||
How to add dynamic filters to a dashboard?
|
||||
------------------------------------------
|
||||
|
||||
It's easy: use the ``Filter Box`` widget, build a slice, and add it to your
|
||||
dashboard.
|
||||
|
||||
The ``Filter Box`` widget allows you to define a query to populate dropdowns
|
||||
that can be use for filtering. To build the list of distinct values, we
|
||||
run a query, and sort the result by the metric you provide, sorting
|
||||
descending.
|
||||
|
||||
The widget also has a checkbox ``Date Filter``, which enables time filtering
|
||||
capabilities to your dashboard. After checking the box and refreshing, you'll
|
||||
see a ``from`` and a ``to`` dropdown show up.
|
||||
|
||||
But what about if you don't want certain widgets to get filtered on your
|
||||
dashboard? You can do that by editing your dashboard, and in the form,
|
||||
edit the ``JSON Metadata`` field, more specifically the
|
||||
``filter_immune_slices`` key, that receives an array of sliceIds that should
|
||||
never be affected by any dashboard level filtering.
|
||||
|
||||
|
||||
..code::
|
||||
|
||||
{
|
||||
"filter_immune_slices": [324, 65, 92],
|
||||
"expanded_slices": {},
|
||||
"filter_immune_slice_fields": {
|
||||
"177": ["country_name", "__from", "__to"],
|
||||
"32": ["__from", "__to"]
|
||||
}
|
||||
}
|
||||
|
||||
In the json blob above, slices 324, 65 and 92 won't be affected by any
|
||||
dashboard level filtering.
|
||||
|
||||
Now note the ``filter_immune_slice_fields`` key. This one allows you to
|
||||
be more specific and define for a specific slice_id, which filter fields
|
||||
should be disregarded.
|
||||
|
||||
Note the use of the ``__from`` and ``__to`` keywords, those are reserved
|
||||
for dealing with the time boundary filtering mentioned above.
|
||||
|
||||
But what happens with filtering when dealing with slices coming from
|
||||
different tables or databases? If the column name is shared, the filter will
|
||||
be applied, it's as simple as that.
|
||||
|
|
Loading…
Reference in New Issue