diff --git a/superset/assets/spec/javascripts/sqllab/VisualizeModal_spec.jsx b/superset/assets/spec/javascripts/sqllab/VisualizeModal_spec.jsx index cacebeb33..b80a96260 100644 --- a/superset/assets/spec/javascripts/sqllab/VisualizeModal_spec.jsx +++ b/superset/assets/spec/javascripts/sqllab/VisualizeModal_spec.jsx @@ -272,6 +272,7 @@ describe('VisualizeModal', () => { schema: 'test_schema', sql: wrapper.instance().props.query.sql, dbId: wrapper.instance().props.query.dbId, + templateParams: wrapper.instance().props.templateParams, }); }); diff --git a/superset/assets/src/SqlLab/components/VisualizeModal.jsx b/superset/assets/src/SqlLab/components/VisualizeModal.jsx index 4bb7a8424..14ba0c0e0 100644 --- a/superset/assets/src/SqlLab/components/VisualizeModal.jsx +++ b/superset/assets/src/SqlLab/components/VisualizeModal.jsx @@ -133,6 +133,7 @@ class VisualizeModal extends React.PureComponent { columns: this.state.columns, sql: this.props.query.sql, dbId: this.props.query.dbId, + templateParams: this.props.query.templateParams, }; } buildVisualizeAdvise() { diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 269b08741..cef3bd61a 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -272,13 +272,14 @@ class SqlaTable(Model, BaseDatasource): schema = Column(String(255)) sql = Column(Text) is_sqllab_view = Column(Boolean, default=False) + template_params = Column(Text) baselink = 'tablemodelview' export_fields = ( 'table_name', 'main_dttm_col', 'description', 'default_endpoint', 'database_id', 'offset', 'cache_timeout', 'schema', - 'sql', 'params') + 'sql', 'params', 'template_params') export_parent = 'database' export_children = ['metrics', 'columns'] @@ -491,8 +492,8 @@ class SqlaTable(Model, BaseDatasource): 'to_dttm': to_dttm, 'filter': filter, 'columns': {col.column_name: col for col in self.columns}, - } + template_kwargs.update(self.template_params_dict) template_processor = self.get_template_processor(**template_kwargs) db_engine_spec = self.database.db_engine_spec diff --git a/superset/connectors/sqla/views.py b/superset/connectors/sqla/views.py index 2f1ef8d72..f0f5afb79 100644 --- a/superset/connectors/sqla/views.py +++ b/superset/connectors/sqla/views.py @@ -173,7 +173,7 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa 'fetch_values_predicate', 'database', 'schema', 'description', 'owner', 'main_dttm_col', 'default_endpoint', 'offset', 'cache_timeout', - 'is_sqllab_view', + 'is_sqllab_view', 'template_params', ] base_filters = [['id', DatasourceFilter, lambda: []]] show_columns = edit_columns + ['perm'] @@ -220,6 +220,9 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa 'is_sqllab_view': _( "Whether the table was generated by the 'Visualize' flow " 'in SQL Lab'), + 'template_params': _( + 'A set of parameters that become available in the query using ' + 'Jinja templating syntax'), } label_columns = { 'slices': _('Associated Charts'), @@ -238,6 +241,7 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa 'main_dttm_col': _('Main Datetime Column'), 'description': _('Description'), 'is_sqllab_view': _('SQL Lab View'), + 'template_params': _('Template parameters'), } def pre_add(self, table): diff --git a/superset/migrations/versions/e502db2af7be_add_template_params_to_tables.py b/superset/migrations/versions/e502db2af7be_add_template_params_to_tables.py new file mode 100644 index 000000000..b7fcce6c2 --- /dev/null +++ b/superset/migrations/versions/e502db2af7be_add_template_params_to_tables.py @@ -0,0 +1,26 @@ +"""add template_params to tables + +Revision ID: e502db2af7be +Revises: 5ccf602336a0 +Create Date: 2018-05-09 23:45:14.296283 + +""" + +# revision identifiers, used by Alembic. +revision = 'e502db2af7be' +down_revision = '5ccf602336a0' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('tables', + sa.Column('template_params', sa.Text(), nullable=True)) + + +def downgrade(): + try: + op.drop_column('tables', 'template_params') + except Exception as e: + logging.warning(str(e)) diff --git a/superset/models/helpers.py b/superset/models/helpers.py index 1431023be..dd2a13bd4 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -25,6 +25,15 @@ from superset import security_manager from superset.utils import QueryStatus +def json_to_dict(json_str): + if json_str: + val = re.sub(',[ \t\r\n]+}', '}', json_str) + val = re.sub(',[ \t\r\n]+\]', ']', val) + return json.loads(val) + else: + return {} + + class ImportMixin(object): export_parent = None # The name of the attribute @@ -216,12 +225,11 @@ class ImportMixin(object): @property def params_dict(self): - if self.params: - params = re.sub(',[ \t\r\n]+}', '}', self.params) - params = re.sub(',[ \t\r\n]+\]', ']', params) - return json.loads(params) - else: - return {} + return json_to_dict(self.params) + + @property + def template_params_dict(self): + return json_to_dict(self.template_params) class AuditMixinNullable(AuditMixin): diff --git a/superset/views/core.py b/superset/views/core.py index d290ecc9c..cdc4a352a 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2144,6 +2144,7 @@ class Superset(BaseSupersetView): SqlaTable = ConnectorRegistry.sources['table'] data = json.loads(request.form.get('data')) table_name = data.get('datasourceName') + template_params = data.get('templateParams') table = ( db.session.query(SqlaTable) .filter_by(table_name=table_name) @@ -2153,6 +2154,7 @@ class Superset(BaseSupersetView): table = SqlaTable(table_name=table_name) table.database_id = data.get('dbId') table.schema = data.get('schema') + table.template_params = data.get('templateParams') table.is_sqllab_view = True q = SupersetQuery(data.get('sql')) table.sql = q.stripped()