Filtering out SQLLab views out of table list view by default (#4746)

* Filtering out SQLLab views out of table list view by default

This adds a `is_sqllab_view` flag in the "tables" table, and makes the
filters those out in the Tables list view.

The filter showing on top may be a bit confusing, but certainly less
than seeing lots of user generated views.

* flake

* Addressing comments
This commit is contained in:
Maxime Beauchemin 2018-04-12 13:52:47 -07:00 committed by GitHub
parent 2a95d203ad
commit b04359003e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 129 additions and 48 deletions

View File

@ -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 (
<div className="container">
<Panel header={<h3>{t('Create a new slice')}</h3>}>
<Grid>
<Row>
<Col xs={12} sm={6}>
<div>
<p>{t('Choose a datasource')}</p>
<Select
clearable={false}
name="select-datasource"
onChange={this.changeDatasource.bind(this)}
options={this.props.datasources}
placeholder={t('Choose a datasource')}
value={this.state.datasourceValue}
/>
</div>
<br />
<div>
<p>{t('Choose a visualization type')}</p>
<Select
clearable={false}
name="select-vis-type"
onChange={this.changeVisType.bind(this)}
options={this.vizTypeOptions}
placeholder={t('Choose a visualization type')}
value={this.state.visType}
/>
</div>
<br />
<Button
bsStyle="primary"
disabled={this.isBtnDisabled()}
onClick={this.gotoSlice.bind(this)}
>
{t('Create new slice')}
</Button>
<br /><br />
</Col>
</Row>
</Grid>
<div>
<p>{t('Choose a datasource')}</p>
<div style={styleSelectWidth}>
<Select
clearable={false}
style={styleSelectWidth}
name="select-datasource"
onChange={this.changeDatasource.bind(this)}
options={this.props.datasources}
placeholder={t('Choose a datasource')}
value={this.state.datasourceValue}
width={200}
/>
</div>
<p className="text-muted">
{t(
'If the datasource your are looking for is not ' +
'available in the list, ' +
'follow the instructions on the how to add it on the ')}
<a href="http://superset.apache.org/tutorial.html">{t('Superset tutorial')}</a>
</p>
</div>
<br />
<div>
<p>{t('Choose a visualization type')}</p>
<Select
clearable={false}
name="select-vis-type"
style={styleSelectWidth}
onChange={this.changeVisType.bind(this)}
options={this.vizTypeOptions}
placeholder={t('Choose a visualization type')}
value={this.state.visType}
/>
</div>
<br />
<Button
bsStyle="primary"
disabled={this.isBtnDisabled()}
onClick={this.gotoSlice.bind(this)}
>
{t('Create new slice')}
</Button>
<br /><br />
</Panel>
</div>
);

View File

@ -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"""

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -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')

View File

@ -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)