Re-enable pylint for some model files (#8770)

* Allow id as a valid name for pylint

* Re-enable pylint for superset/models/core.py

* re-enable pylint for superset/models/sql_lab.py

* re-enable pylint for superset/models/schedules.py

* re-enable pylint for superset/models/helpers.py

* re-enable pylint for superset/models/annotations.py

* re-enable pylint on superset/models/tags.py

* a couple more fixes after black formatting...

* Add another inline pylint disable

* Fix black

* Move to inline disables for 'id' attribute on models

* Fix lint disables after black reformatted them
This commit is contained in:
Will Barrett 2019-12-10 13:24:45 -08:00 committed by Maxime Beauchemin
parent 6c130b32ad
commit adf2cc2039
6 changed files with 167 additions and 133 deletions

View File

@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W
"""a collection of Annotation-related models"""
from flask_appbuilder import Model
from sqlalchemy import Column, DateTime, ForeignKey, Index, Integer, String, Text
@ -28,7 +27,7 @@ class AnnotationLayer(Model, AuditMixinNullable):
"""A logical namespace for a set of annotations"""
__tablename__ = "annotation_layer"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
name = Column(String(250))
descr = Column(Text)
@ -41,7 +40,7 @@ class Annotation(Model, AuditMixinNullable):
"""Time-related annotation"""
__tablename__ = "annotation"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
start_dttm = Column(DateTime)
end_dttm = Column(DateTime)
layer_id = Column(Integer, ForeignKey("annotation_layer.id"), nullable=False)

View File

@ -14,7 +14,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W
# pylint: disable=line-too-long,unused-argument,ungrouped-imports
"""A collection of ORM sqlalchemy models for Superset"""
import json
import logging
@ -66,8 +66,9 @@ from superset.utils import cache as cache_util, core as utils
from superset.viz import BaseViz, viz_types
if TYPE_CHECKING:
from superset.connectors.base.models import BaseDatasource
from superset.connectors.base.models import ( # pylint: disable=unused-import
BaseDatasource,
)
config = app.config
custom_password_store = config["SQLALCHEMY_CUSTOM_PASSWORD_STORE"]
@ -76,6 +77,7 @@ log_query = config["QUERY_LOGGER"]
metadata = Model.metadata # pylint: disable=no-member
PASSWORD_MASK = "X" * 10
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
def set_related_perm(mapper, connection, target):
@ -93,8 +95,8 @@ def copy_dashboard(mapper, connection, target):
if dashboard_id is None:
return
Session = sessionmaker(autoflush=False)
session = Session(bind=connection)
session_class = sessionmaker(autoflush=False)
session = session_class(bind=connection)
new_user = session.query(User).filter_by(id=target.id).first()
# copy template dashboard to user
@ -126,16 +128,16 @@ class Url(Model, AuditMixinNullable):
"""Used for the short url feature"""
__tablename__ = "url"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
url = Column(Text)
class KeyValue(Model):
class KeyValue(Model): # pylint: disable=too-few-public-methods
"""Used for any type of key-value store"""
__tablename__ = "keyvalue"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
value = Column(Text, nullable=False)
@ -144,7 +146,7 @@ class CssTemplate(Model, AuditMixinNullable):
"""CSS templates for dashboards"""
__tablename__ = "css_templates"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
template_name = Column(String(250))
css = Column(Text, default="")
@ -158,12 +160,14 @@ slice_user = Table(
)
class Slice(Model, AuditMixinNullable, ImportMixin):
class Slice(
Model, AuditMixinNullable, ImportMixin
): # pylint: disable=too-many-public-methods
"""A slice is essentially a report or a view on data"""
__tablename__ = "slices"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
slice_name = Column(String(250))
datasource_id = Column(Integer)
datasource_type = Column(String(200))
@ -175,6 +179,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
perm = Column(String(1000))
schema_perm = Column(String(1000))
owners = relationship(security_manager.user_model, secondary=slice_user)
token = ""
export_fields = [
"slice_name",
@ -208,6 +213,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
cache_timeout=self.cache_timeout,
)
# pylint: disable=using-constant-test
@datasource.getter # type: ignore
@utils.memoized
def get_datasource(self) -> Optional["BaseDatasource"]:
@ -230,6 +236,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
datasource = self.datasource
return datasource.url if datasource else None
# pylint: enable=using-constant-test
@property # type: ignore
@utils.memoized
def viz(self) -> BaseViz:
@ -249,7 +257,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
try:
d = self.viz.data
self.token = d.get("token") # type: ignore
except Exception as e:
except Exception as e: # pylint: disable=broad-except
logging.exception(e)
d["error"] = str(e)
return {
@ -275,7 +283,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
form_data: Dict[str, Any] = {}
try:
form_data = json.loads(self.params)
except Exception as e:
except Exception as e: # pylint: disable=broad-except
logging.error("Malformed json in slice's params")
logging.exception(e)
form_data.update(
@ -322,9 +330,8 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
@property
def slice_link(self) -> Markup:
url = self.slice_url
name = escape(self.chart)
return Markup(f'<a href="{url}">{name}</a>')
return Markup(f'<a href="{self.url}">{name}</a>')
def get_viz(self, force: bool = False) -> BaseViz:
"""Creates :py:class:viz.BaseViz object from the url_params_multidict.
@ -392,7 +399,7 @@ class Slice(Model, AuditMixinNullable, ImportMixin):
session.flush()
return slc_to_override.id
session.add(slc_to_import)
logging.info("Final slice: {}".format(slc_to_import.to_json()))
logging.info("Final slice: %s", str(slc_to_import.to_json()))
session.flush()
return slc_to_import.id
@ -423,12 +430,14 @@ dashboard_user = Table(
)
class Dashboard(Model, AuditMixinNullable, ImportMixin):
class Dashboard( # pylint: disable=too-many-instance-attributes
Model, AuditMixinNullable, ImportMixin
):
"""The dashboard object!"""
__tablename__ = "dashboards"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
dashboard_title = Column(String(500))
position_json = Column(utils.MediumText())
description = Column(Text)
@ -470,7 +479,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
return "/superset/dashboard/{}/?preselect_filters={}".format(
self.slug or self.id, filters
)
except Exception:
except Exception: # pylint: disable=broad-except
pass
return f"/superset/dashboard/{self.slug or self.id}/"
@ -485,8 +494,8 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
@property
def sqla_metadata(self) -> None:
# pylint: disable=no-member
metadata = MetaData(bind=self.get_sqla_engine())
metadata.reflect()
meta = MetaData(bind=self.get_sqla_engine())
meta.reflect()
def dashboard_link(self) -> Markup:
title = escape(self.dashboard_title or "<empty>")
@ -523,7 +532,7 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
return {}
@classmethod
def import_obj(
def import_obj( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
cls, dashboard_to_import: "Dashboard", import_time: Optional[int] = None
) -> int:
"""Imports the dashboard from the object to the database.
@ -579,10 +588,10 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
dashboard.position_json = json.dumps(position_data)
logging.info(
"Started import of the dashboard: {}".format(dashboard_to_import.to_json())
"Started import of the dashboard: %s", dashboard_to_import.to_json()
)
session = db.session
logging.info("Dashboard has {} slices".format(len(dashboard_to_import.slices)))
logging.info("Dashboard has %d slices", len(dashboard_to_import.slices))
# copy slices object as Slice.import_slice will mutate the slice
# and will remove the existing dashboard - slice association
slices = copy(dashboard_to_import.slices)
@ -599,9 +608,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
}
for slc in slices:
logging.info(
"Importing slice {} from the dashboard: {}".format(
slc.to_json(), dashboard_to_import.dashboard_title
)
"Importing slice %s from the dashboard: %s",
slc.to_json(),
dashboard_to_import.dashboard_title,
)
remote_slc = remote_id_slice_map.get(slc.id)
new_slc_id = Slice.import_obj(slc, remote_slc, import_time=import_time)
@ -677,14 +686,16 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
existing_dashboard.slices = new_slices
session.flush()
return existing_dashboard.id
else:
dashboard_to_import.slices = new_slices
session.add(dashboard_to_import)
session.flush()
return dashboard_to_import.id # type: ignore
dashboard_to_import.slices = new_slices
session.add(dashboard_to_import)
session.flush()
return dashboard_to_import.id # type: ignore
@classmethod
def export_dashboards(cls, dashboard_ids: List) -> str:
def export_dashboards( # pylint: disable=too-many-locals
cls, dashboard_ids: List
) -> str:
copied_dashboards = []
datasource_ids = set()
for dashboard_id in dashboard_ids:
@ -741,7 +752,9 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
)
class Database(Model, AuditMixinNullable, ImportMixin):
class Database(
Model, AuditMixinNullable, ImportMixin
): # pylint: disable=too-many-public-methods
"""An ORM object that stores Database related information"""
@ -749,7 +762,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
type = "table"
__table_args__ = (UniqueConstraint("database_name"),)
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
verbose_name = Column(String(250), unique=True)
# short unique name, used in permissions
database_name = Column(String(250), unique=True, nullable=False)
@ -763,7 +776,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
allow_ctas = Column(Boolean, default=False)
allow_dml = Column(Boolean, default=False)
force_ctas_schema = Column(String(250))
allow_multi_schema_metadata_fetch = Column(Boolean, default=False)
allow_multi_schema_metadata_fetch = Column( # pylint: disable=invalid-name
Boolean, default=False
)
extra = Column(
Text,
default=textwrap.dedent(
@ -836,8 +851,8 @@ class Database(Model, AuditMixinNullable, ImportMixin):
@property
def backend(self) -> str:
url = make_url(self.sqlalchemy_uri_decrypted)
return url.get_backend_name()
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
return sqlalchemy_url.get_backend_name()
@property
def metadata_cache_timeout(self) -> Dict[str, Any]:
@ -864,12 +879,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
return self.get_extra().get("default_schemas", [])
@classmethod
def get_password_masked_url_from_uri(cls, uri: str):
url = make_url(uri)
return cls.get_password_masked_url(url)
def get_password_masked_url_from_uri(cls, uri: str): # pylint: disable=invalid-name
sqlalchemy_url = make_url(uri)
return cls.get_password_masked_url(sqlalchemy_url)
@classmethod
def get_password_masked_url(cls, url: URL) -> URL:
def get_password_masked_url(
cls, url: URL # pylint: disable=redefined-outer-name
) -> URL:
url_copy = deepcopy(url)
if url_copy.password is not None:
url_copy.password = PASSWORD_MASK
@ -884,7 +901,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
self.sqlalchemy_uri = str(conn) # hides the password
def get_effective_user(
self, url: URL, user_name: Optional[str] = None
self,
url: URL, # pylint: disable=redefined-outer-name
user_name: Optional[str] = None,
) -> Optional[str]:
"""
Get the effective user, especially during impersonation.
@ -914,18 +933,18 @@ class Database(Model, AuditMixinNullable, ImportMixin):
source: Optional[int] = None,
) -> Engine:
extra = self.get_extra()
url = make_url(self.sqlalchemy_uri_decrypted)
url = self.db_engine_spec.adjust_database_uri(url, schema)
effective_username = self.get_effective_user(url, user_name)
sqlalchemy_url = make_url(self.sqlalchemy_uri_decrypted)
sqlalchemy_url = self.db_engine_spec.adjust_database_uri(sqlalchemy_url, schema)
effective_username = self.get_effective_user(sqlalchemy_url, user_name)
# If using MySQL or Presto for example, will set url.username
# If using Hive, will not do anything yet since that relies on a
# configuration parameter instead.
self.db_engine_spec.modify_url_for_impersonation(
url, self.impersonate_user, effective_username
sqlalchemy_url, self.impersonate_user, effective_username
)
masked_url = self.get_password_masked_url(url)
logging.info("Database.get_sqla_engine(). Masked URL: {0}".format(masked_url))
masked_url = self.get_password_masked_url(sqlalchemy_url)
logging.info("Database.get_sqla_engine(). Masked URL: %s", str(masked_url))
params = extra.get("engine_params", {})
if nullpool:
@ -935,7 +954,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
configuration: Dict[str, Any] = {}
configuration.update(
self.db_engine_spec.get_configuration_for_impersonation(
str(url), self.impersonate_user, effective_username
str(sqlalchemy_url), self.impersonate_user, effective_username
)
)
if configuration:
@ -945,12 +964,11 @@ class Database(Model, AuditMixinNullable, ImportMixin):
params.update(self.get_encrypted_extra())
DB_CONNECTION_MUTATOR = config["DB_CONNECTION_MUTATOR"]
if DB_CONNECTION_MUTATOR:
url, params = DB_CONNECTION_MUTATOR(
url, params, effective_username, security_manager, source
sqlalchemy_url, params = DB_CONNECTION_MUTATOR(
sqlalchemy_url, params, effective_username, security_manager, source
)
return create_engine(url, **params)
return create_engine(sqlalchemy_url, **params)
def get_reserved_words(self) -> Set[str]:
return self.get_dialect().preparer.reserved_words
@ -958,7 +976,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
def get_quoter(self):
return self.get_dialect().identifier_preparer.quote
def get_df(
def get_df( # pylint: disable=too-many-locals
self, sql: str, schema: str, mutator: Optional[Callable] = None
) -> pd.DataFrame:
sqls = [str(s).strip(" ;") for s in sqlparse.parse(sql)]
@ -982,9 +1000,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
with closing(engine.raw_connection()) as conn:
with closing(conn.cursor()) as cursor:
for sql in sqls[:-1]:
_log_query(sql)
self.db_engine_spec.execute(cursor, sql)
for sql_ in sqls[:-1]:
_log_query(sql_)
self.db_engine_spec.execute(cursor, sql_)
cursor.fetchall()
_log_query(sqls[-1])
@ -1012,12 +1030,14 @@ class Database(Model, AuditMixinNullable, ImportMixin):
sql = str(qry.compile(engine, compile_kwargs={"literal_binds": True}))
if engine.dialect.identifier_preparer._double_percents:
if (
engine.dialect.identifier_preparer._double_percents # pylint: disable=protected-access
):
sql = sql.replace("%%", "%")
return sql
def select_star(
def select_star( # pylint: disable=too-many-arguments
self,
table_name: str,
sql: Optional[str] = None,
@ -1109,7 +1129,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
return [
utils.DatasourceName(table=table, schema=schema) for table in tables
]
except Exception as e:
except Exception as e: # pylint: disable=broad-except
logging.exception(e)
@cache_util.memoized_func(
@ -1139,7 +1159,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
database=self, inspector=self.inspector, schema=schema
)
return [utils.DatasourceName(table=view, schema=schema) for view in views]
except Exception as e:
except Exception as e: # pylint: disable=broad-except
logging.exception(e)
@cache_util.memoized_func(
@ -1232,7 +1252,9 @@ class Database(Model, AuditMixinNullable, ImportMixin):
) -> List[Dict[str, Any]]:
return self.inspector.get_foreign_keys(table_name, schema)
def get_schema_access_for_csv_upload(self) -> List[str]:
def get_schema_access_for_csv_upload( # pylint: disable=invalid-name
self
) -> List[str]:
return self.get_extra().get("schemas_allowed_for_csv_upload", [])
@property
@ -1269,13 +1291,13 @@ sqla.event.listen(Database, "after_insert", security_manager.set_perm)
sqla.event.listen(Database, "after_update", security_manager.set_perm)
class Log(Model):
class Log(Model): # pylint: disable=too-few-public-methods
"""ORM object used to log Superset actions to the database"""
__tablename__ = "logs"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
action = Column(String(512))
user_id = Column(Integer, ForeignKey("ab_user.id"))
dashboard_id = Column(Integer)
@ -1289,10 +1311,10 @@ class Log(Model):
referrer = Column(String(1024))
class FavStar(Model):
class FavStar(Model): # pylint: disable=too-few-public-methods
__tablename__ = "favstar"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
user_id = Column(Integer, ForeignKey("ab_user.id"))
class_name = Column(String(50))
obj_id = Column(Integer)
@ -1303,7 +1325,7 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
"""ORM model for the access requests for datasources and dbs."""
__tablename__ = "access_request"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
datasource_id = Column(Integer)
datasource_type = Column(String(200))
@ -1337,33 +1359,33 @@ class DatasourceAccessRequest(Model, AuditMixinNullable):
action_list = ""
perm = self.datasource.perm # pylint: disable=no-member
pv = security_manager.find_permission_view_menu("datasource_access", perm)
for r in pv.role:
if r.name in self.ROLES_BLACKLIST:
for role in pv.role:
if role.name in self.ROLES_BLACKLIST:
continue
# pylint: disable=no-member
url = (
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_grant={r.name}"
f"created_by={self.created_by.username}&role_to_grant={role.name}"
)
href = '<a href="{}">Grant {} Role</a>'.format(url, r.name)
action_list = action_list + "<li>" + href + "</li>"
link = '<a href="{}">Grant {} Role</a>'.format(href, role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"
@property
def user_roles(self) -> str:
action_list = ""
for r in self.created_by.roles: # pylint: disable=no-member
for role in self.created_by.roles: # pylint: disable=no-member
# pylint: disable=no-member
url = (
href = (
f"/superset/approve?datasource_type={self.datasource_type}&"
f"datasource_id={self.datasource_id}&"
f"created_by={self.created_by.username}&role_to_extend={r.name}"
f"created_by={self.created_by.username}&role_to_extend={role.name}"
)
href = '<a href="{}">Extend {} Role</a>'.format(url, r.name)
if r.name in self.ROLES_BLACKLIST:
href = "{} Role".format(r.name)
action_list = action_list + "<li>" + href + "</li>"
link = '<a href="{}">Extend {} Role</a>'.format(href, role.name)
if role.name in self.ROLES_BLACKLIST:
link = "{} Role".format(role.name)
action_list = action_list + "<li>" + link + "</li>"
return "<ul>" + action_list + "</ul>"

View File

@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W
"""a collection of model-related helper classes and functions"""
import json
import logging
@ -22,6 +21,8 @@ import re
from datetime import datetime
from typing import List, Optional
# isort and pylint disagree, isort should win
# pylint: disable=ungrouped-imports
import humanize
import sqlalchemy as sa
import yaml
@ -38,10 +39,12 @@ from superset.utils.core 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)
val = re.sub(
",[ \t\r\n]+\]", "]", val # pylint: disable=anomalous-backslash-in-string
)
return json.loads(val)
else:
return {}
return {}
class ImportMixin(object):
@ -83,24 +86,24 @@ class ImportMixin(object):
if not include_parent_ref:
parent_ref = cls.__mapper__.relationships.get(cls.export_parent)
if parent_ref:
parent_excludes = {c.name for c in parent_ref.local_columns}
parent_excludes = {column.name for column in parent_ref.local_columns}
def formatter(c):
def formatter(column):
return (
"{0} Default ({1})".format(str(c.type), c.default.arg)
if c.default
else str(c.type)
"{0} Default ({1})".format(str(column.type), column.default.arg)
if column.default
else str(column.type)
)
schema = {
c.name: formatter(c)
for c in cls.__table__.columns
if (c.name in cls.export_fields and c.name not in parent_excludes)
column.name: formatter(column)
for column in cls.__table__.columns
if (column.name in cls.export_fields and column.name not in parent_excludes)
}
if recursive:
for c in cls.export_children:
child_class = cls.__mapper__.relationships[c].argument.class_
schema[c] = [
for column in cls.export_children:
child_class = cls.__mapper__.relationships[column].argument.class_
schema[column] = [
child_class.export_schema(
recursive=recursive, include_parent_ref=include_parent_ref
)
@ -108,8 +111,12 @@ class ImportMixin(object):
return schema
@classmethod
def import_from_dict(cls, session, dict_rep, parent=None, recursive=True, sync=[]):
def import_from_dict(
cls, session, dict_rep, parent=None, recursive=True, sync=None
): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches
"""Import obj from a dictionary"""
if sync is None:
sync = []
parent_refs = cls._parent_foreign_key_mappings()
export_fields = set(cls.export_fields) | set(parent_refs.keys())
new_children = {
@ -126,10 +133,10 @@ class ImportMixin(object):
if not parent:
if cls.export_parent:
for p in parent_refs.keys():
if p not in dict_rep:
for prnt in parent_refs.keys():
if prnt not in dict_rep:
raise RuntimeError(
"{0}: Missing field {1}".format(cls.__name__, p)
"{0}: Missing field {1}".format(cls.__name__, prnt)
)
else:
# Set foreign keys to parent obj
@ -182,10 +189,10 @@ class ImportMixin(object):
# Recursively create children
if recursive:
for c in cls.export_children:
child_class = cls.__mapper__.relationships[c].argument.class_
for child in cls.export_children:
child_class = cls.__mapper__.relationships[child].argument.class_
added = []
for c_obj in new_children.get(c, []):
for c_obj in new_children.get(child, []):
added.append(
child_class.import_from_dict(
session=session, dict_rep=c_obj, parent=obj, sync=sync
@ -193,8 +200,10 @@ class ImportMixin(object):
)
# If children should get synced, delete the ones that did not
# get updated.
if c in sync and not is_new_obj:
back_refs = child_class._parent_foreign_key_mappings()
if child in sync and not is_new_obj:
back_refs = (
child_class._parent_foreign_key_mappings() # pylint: disable=protected-access
)
delete_filters = [
getattr(child_class, k) == getattr(obj, back_refs.get(k))
for k in back_refs.keys()
@ -203,7 +212,7 @@ class ImportMixin(object):
session.query(child_class).filter(and_(*delete_filters))
).difference(set(added))
for o in to_delete:
logging.info("Deleting %s %s", c, str(obj))
logging.info("Deleting %s %s", child, str(obj))
session.delete(o)
return obj
@ -234,16 +243,16 @@ class ImportMixin(object):
)
}
if recursive:
for c in self.export_children:
for cld in self.export_children:
# sorting to make lists of children stable
dict_rep[c] = sorted(
dict_rep[cld] = sorted(
[
child.export_to_dict(
recursive=recursive,
include_parent_ref=include_parent_ref,
include_defaults=include_defaults,
)
for child in getattr(self, c)
for child in getattr(self, cld)
],
key=lambda k: sorted(str(k.items())),
)
@ -276,7 +285,7 @@ class ImportMixin(object):
try:
if g.user:
self.owners = [g.user]
except Exception:
except Exception: # pylint: disable=broad-except
self.owners = []
@property
@ -288,6 +297,13 @@ class ImportMixin(object):
return json_to_dict(self.template_params)
def _user_link(user): # pylint: disable=no-self-use
if not user:
return ""
url = "/superset/profile/{}/".format(user.username)
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
class AuditMixinNullable(AuditMixin):
"""Altering the AuditMixin to use nullable fields
@ -319,12 +335,6 @@ class AuditMixinNullable(AuditMixin):
nullable=True,
)
def _user_link(self, user):
if not user:
return ""
url = "/superset/profile/{}/".format(user.username)
return Markup('<a href="{}">{}</a>'.format(url, escape(user) or ""))
def changed_by_name(self):
if self.created_by:
return escape("{}".format(self.created_by))
@ -332,11 +342,11 @@ class AuditMixinNullable(AuditMixin):
@renders("created_by")
def creator(self):
return self._user_link(self.created_by)
return _user_link(self.created_by)
@property
def changed_by_(self):
return self._user_link(self.changed_by)
return _user_link(self.changed_by)
@renders("changed_on")
def changed_on_(self):
@ -351,14 +361,14 @@ class AuditMixinNullable(AuditMixin):
return Markup(f'<span class="no-wrap">{self.changed_on_humanized}</span>')
class QueryResult(object):
class QueryResult(object): # pylint: disable=too-few-public-methods
"""Object returned by the query interface"""
def __init__(
def __init__( # pylint: disable=too-many-arguments
self, df, query, duration, status=QueryStatus.SUCCESS, error_message=None
):
self.df = df
self.df = df # pylint: disable=invalid-name
self.query = query
self.duration = duration
self.status = status
@ -374,7 +384,7 @@ class ExtraJSONMixin:
def extra(self):
try:
return json.loads(self.extra_json)
except Exception:
except Exception: # pylint: disable=broad-except
return {}
def set_extra_json(self, d):

View File

@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W
"""Models for scheduled execution of jobs"""
import enum
@ -51,7 +50,7 @@ class EmailSchedule:
__tablename__ = "email_schedules"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
active = Column(Boolean, default=True, index=True)
crontab = Column(String(50))
@ -92,3 +91,4 @@ def get_scheduler_model(report_type):
return DashboardEmailSchedule
elif report_type == ScheduleType.slice.value:
return SliceEmailSchedule
return None

View File

@ -14,11 +14,11 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W
"""A collection of ORM sqlalchemy models for SQL Lab"""
import re
from datetime import datetime
# pylint: disable=ungrouped-imports
import simplejson as json
import sqlalchemy as sqla
from flask import Markup
@ -48,7 +48,7 @@ class Query(Model, ExtraJSONMixin):
table may represent multiple SQL statements executed sequentially"""
__tablename__ = "query"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
client_id = Column(String(11), unique=True, nullable=False)
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)
@ -149,7 +149,7 @@ class SavedQuery(Model, AuditMixinNullable, ExtraJSONMixin):
"""ORM model for SQL query"""
__tablename__ = "saved_query"
id = Column(Integer, primary_key=True)
id = Column(Integer, primary_key=True) # pylint: disable=invalid-name
user_id = Column(Integer, ForeignKey("ab_user.id"), nullable=True)
db_id = Column(Integer, ForeignKey("dbs.id"), nullable=True)
schema = Column(String(128))
@ -194,7 +194,9 @@ class TabState(Model, AuditMixinNullable, ExtraJSONMixin):
__tablename__ = "tab_state"
# basic info
id = Column(Integer, primary_key=True, autoincrement=True)
id = Column( # pylint: disable=invalid-name
Integer, primary_key=True, autoincrement=True
)
user_id = Column(Integer, ForeignKey("ab_user.id"))
label = Column(String(256))
active = Column(Boolean, default=False)
@ -245,7 +247,9 @@ class TableSchema(Model, AuditMixinNullable, ExtraJSONMixin):
__tablename__ = "table_schema"
id = Column(Integer, primary_key=True, autoincrement=True)
id = Column( # pylint: disable=invalid-name
Integer, primary_key=True, autoincrement=True
)
tab_state_id = Column(Integer, ForeignKey("tab_state.id", ondelete="CASCADE"))
database_id = Column(Integer, ForeignKey("dbs.id"), nullable=False)

View File

@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# pylint: disable=C,R,W,no-init
from __future__ import absolute_import, division, print_function, unicode_literals
import enum