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:
parent
2a95d203ad
commit
b04359003e
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue