diff --git a/superset/assets/javascripts/addSlice/AddSliceContainer.jsx b/superset/assets/javascripts/addSlice/AddSliceContainer.jsx index 051693468..6b1f5e7a8 100644 --- a/superset/assets/javascripts/addSlice/AddSliceContainer.jsx +++ b/superset/assets/javascripts/addSlice/AddSliceContainer.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Panel, Grid, Row, Col } from 'react-bootstrap'; +import { Button, Panel } from 'react-bootstrap'; import Select from 'react-virtualized-select'; import visTypes from '../explore/stores/visTypes'; import { t } from '../locales'; @@ -12,6 +12,8 @@ const propTypes = { })).isRequired, }; +const styleSelectWidth = { width: 300 }; + export default class AddSliceContainer extends React.PureComponent { constructor(props) { super(props); @@ -55,44 +57,50 @@ export default class AddSliceContainer extends React.PureComponent { return (
{t('Create a new slice')}}> - - - -
-

{t('Choose a datasource')}

- -
-
- -

- -
-
+
+

{t('Choose a datasource')}

+
+ +
+
+ +

); diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py index 7a115988a..68a020e36 100644 --- a/superset/connectors/base/models.py +++ b/superset/connectors/base/models.py @@ -207,6 +207,10 @@ class BaseDatasource(AuditMixinNullable, ImportMixin): values in filters in the explore view""" raise NotImplementedError() + @staticmethod + def default_query(qry): + return qry + class BaseColumn(AuditMixinNullable, ImportMixin): """Interface for column""" diff --git a/superset/connectors/connector_registry.py b/superset/connectors/connector_registry.py index 0a6291ab4..79f876acf 100644 --- a/superset/connectors/connector_registry.py +++ b/superset/connectors/connector_registry.py @@ -33,8 +33,10 @@ class ConnectorRegistry(object): def get_all_datasources(cls, session): datasources = [] for source_type in ConnectorRegistry.sources: - datasources.extend( - session.query(ConnectorRegistry.sources[source_type]).all()) + source_class = ConnectorRegistry.sources[source_type] + qry = session.query(source_class) + qry = source_class.default_query(qry) + datasources.extend(qry.all()) return datasources @classmethod diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 703304864..8ed708079 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -268,6 +268,7 @@ class SqlaTable(Model, BaseDatasource): foreign_keys=[database_id]) schema = Column(String(255)) sql = Column(Text) + is_sqllab_view = Column(Boolean, default=False) baselink = 'tablemodelview' @@ -819,6 +820,10 @@ class SqlaTable(Model, BaseDatasource): query = query.filter_by(schema=schema) return query.all() + @staticmethod + def default_query(qry): + return qry.filter_by(is_sqllab_view=False) + sa.event.listen(SqlaTable, 'after_insert', set_perm) sa.event.listen(SqlaTable, 'after_update', set_perm) diff --git a/superset/connectors/sqla/views.py b/superset/connectors/sqla/views.py index 3d4779535..2b8da0e45 100644 --- a/superset/connectors/sqla/views.py +++ b/superset/connectors/sqla/views.py @@ -171,12 +171,15 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa 'table_name', 'sql', 'filter_select_enabled', 'slices', 'fetch_values_predicate', 'database', 'schema', 'description', 'owner', - 'main_dttm_col', 'default_endpoint', 'offset', 'cache_timeout'] + 'main_dttm_col', 'default_endpoint', 'offset', 'cache_timeout', + 'is_sqllab_view', + ] + base_filters = [['id', DatasourceFilter, lambda: []]] show_columns = edit_columns + ['perm'] related_views = [TableColumnInlineView, SqlMetricInlineView] base_order = ('changed_on', 'desc') search_columns = ( - 'database', 'schema', 'table_name', 'owner', + 'database', 'schema', 'table_name', 'owner', 'is_sqllab_view', ) description_columns = { 'slices': _( @@ -213,8 +216,10 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa "Whether to populate the filter's dropdown in the explore " "view's filter section with a list of distinct values fetched " 'from the backend on the fly'), + 'is_sqllab_view': _( + "Whether the table was generated by the 'Visualize' flow " + 'in SQL Lab'), } - base_filters = [['id', DatasourceFilter, lambda: []]] label_columns = { 'slices': _('Associated Charts'), 'link': _('Table'), @@ -231,6 +236,7 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa 'owner': _('Owner'), 'main_dttm_col': _('Main Datetime Column'), 'description': _('Description'), + 'is_sqllab_view': _('SQL Lab View'), } def pre_add(self, table): @@ -298,13 +304,14 @@ class TableModelView(DatasourceModelView, DeleteMixin, YamlExportMixin): # noqa return redirect('/tablemodelview/list/') -appbuilder.add_view( - TableModelView, +appbuilder.add_view_no_menu(TableModelView) +appbuilder.add_link( 'Tables', label=__('Tables'), + href='/tablemodelview/list/?_flt_1_is_sqllab_view=y', + icon='fa-upload', category='Sources', category_label=__('Sources'), - icon='fa-table', -) + category_icon='fa-table') appbuilder.add_separator('Sources') diff --git a/superset/migrations/versions/130915240929_is_sqllab_viz_flow.py b/superset/migrations/versions/130915240929_is_sqllab_viz_flow.py new file mode 100644 index 000000000..be17c50c0 --- /dev/null +++ b/superset/migrations/versions/130915240929_is_sqllab_viz_flow.py @@ -0,0 +1,54 @@ +"""is_sqllab_view + +Revision ID: 130915240929 +Revises: f231d82b9b26 +Create Date: 2018-04-03 08:19:34.098789 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.ext.declarative import declarative_base + +from superset import db + +# revision identifiers, used by Alembic. +revision = '130915240929' +down_revision = 'f231d82b9b26' + +Base = declarative_base() + + +class Table(Base): + """Declarative class to do query in upgrade""" + __tablename__ = 'tables' + id = sa.Column(sa.Integer, primary_key=True) + sql = sa.Column(sa.Text) + is_sqllab_view = sa.Column(sa.Boolean()) + + +def upgrade(): + bind = op.get_bind() + op.add_column( + 'tables', + sa.Column( + 'is_sqllab_view', + sa.Boolean(), + nullable=True, + default=False, + server_default=sa.false(), + ), + ) + + session = db.Session(bind=bind) + + # Use Slice class defined here instead of models.Slice + for tbl in session.query(Table).all(): + if tbl.sql: + tbl.is_sqllab_view = True + session.merge(tbl) + session.commit() + db.session.close() + + +def downgrade(): + op.drop_column('tables', 'is_sqllab_view') diff --git a/superset/views/core.py b/superset/views/core.py index 6ffa4ee2c..6629dbd30 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2144,6 +2144,7 @@ class Superset(BaseSupersetView): table = SqlaTable(table_name=table_name) table.database_id = data.get('dbId') table.schema = data.get('schema') + table.is_sqllab_view = True q = SupersetQuery(data.get('sql')) table.sql = q.stripped() db.session.add(table)