From ec8abe70029301681e083e76ff0a30afac17b5bf Mon Sep 17 00:00:00 2001 From: Manuel Silva Date: Tue, 18 Sep 2018 02:36:47 +1000 Subject: [PATCH] Adds a new macro to allow getting filter values easily (#5547) * Adds new macro to get filter values from "filters" and "extra_filters" Adds test for filter_values macro Adds doco for filter_values Changes filter_values return type to be a list rather than string * Makes return value type consistent - filter_values always return a list --- docs/sqllab.rst | 2 ++ superset/jinja_context.py | 45 ++++++++++++++++++++++++++- tests/macro_tests.py | 65 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/macro_tests.py diff --git a/docs/sqllab.rst b/docs/sqllab.rst index 9230b2c73ab77..2ba9ac2c986be 100644 --- a/docs/sqllab.rst +++ b/docs/sqllab.rst @@ -67,6 +67,8 @@ Superset's Jinja context: .. autofunction:: superset.jinja_context.url_param +.. autofunction:: superset.jinja_context.filter_values + Extending macros '''''''''''''''' diff --git a/superset/jinja_context.py b/superset/jinja_context.py index b700515284bcb..27104ccb04119 100644 --- a/superset/jinja_context.py +++ b/superset/jinja_context.py @@ -61,8 +61,50 @@ def current_username(): return g.user.username -class BaseTemplateProcessor(object): +def filter_values(column, default=None): + """ Gets a values for a particular filter as a list + + This is useful if: + - you want to use a filter box to filter a query where the name of filter box + column doesn't match the one in the select statement + - you want to have the ability for filter inside the main query for speed purposes + + This searches for "filters" and "extra_filters" in form_data for a match + + Usage example: + * SELECT action, count(*) as times + FROM logs + WHERE action in ( {{ "'" + "','".join(filter_values('action_type')) + "'" ) + GROUP BY 1 + + :param column: column/filter name to lookup + :type column: str + :param default: default value to return if there's no matching columns + :type default: str + :return: returns a list of filter values + :rtype: list + """ + form_data = json.loads(request.form.get('form_data', '{}')) + return_val = [] + for filter_type in ['filters', 'extra_filters']: + if filter_type not in form_data: + continue + + for f in form_data[filter_type]: + if f['col'] == column: + for v in f['val']: + return_val.append(v) + + if return_val: + return return_val + if default: + return [default] + else: + return [] + + +class BaseTemplateProcessor(object): """Base class for database-specific jinja context There's this bit of magic in ``process_template`` that instantiates only @@ -90,6 +132,7 @@ def __init__(self, database=None, query=None, table=None, **kwargs): 'url_param': url_param, 'current_user_id': current_user_id, 'current_username': current_username, + 'filter_values': filter_values, 'form_data': {}, } self.context.update(kwargs) diff --git a/tests/macro_tests.py b/tests/macro_tests.py new file mode 100644 index 0000000000000..4f77a68e3fcd9 --- /dev/null +++ b/tests/macro_tests.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +from flask import json +from tests.base_tests import SupersetTestCase + +from superset import app +from superset import jinja_context + + +class MacroTestCase(SupersetTestCase): + + def test_filter_values_macro(self): + form_data1 = { + 'extra_filters': [ + {'col': 'my_special_filter', 'op': 'in', 'val': ['foo']}, + ], + 'filters': [ + {'col': 'my_special_filter2', 'op': 'in', 'val': ['bar']}, + ], + } + + form_data2 = { + 'extra_filters': [ + {'col': 'my_special_filter', 'op': 'in', 'val': ['foo', 'bar']}, + ], + } + + form_data3 = { + 'extra_filters': [ + {'col': 'my_special_filter', 'op': 'in', 'val': ['foo', 'bar']}, + ], + 'filters': [ + {'col': 'my_special_filter', 'op': 'in', 'val': ['savage']}, + ], + } + + data1 = {'form_data': json.dumps(form_data1)} + data2 = {'form_data': json.dumps(form_data2)} + data3 = {'form_data': json.dumps(form_data3)} + + with app.test_request_context(data=data1): + filter_values = jinja_context.filter_values('my_special_filter') + self.assertEqual(filter_values, ['foo']) + + filter_values = jinja_context.filter_values('my_special_filter2') + self.assertEqual(filter_values, ['bar']) + + filter_values = jinja_context.filter_values('') + self.assertEqual(filter_values, []) + + with app.test_request_context(data=data2): + filter_values = jinja_context.filter_values('my_special_filter') + self.assertEqual(filter_values, ['foo', 'bar']) + + with app.test_request_context(data=data3): + filter_values = jinja_context.filter_values('my_special_filter') + self.assertEqual(filter_values, ['savage', 'foo', 'bar']) + + with app.test_request_context(): + filter_values = jinja_context.filter_values('nonexistent_filter', 'foo') + self.assertEqual(filter_values, ['foo'])