class_docstr: Datasource-related views
class_docstr_tok: ["Datasource-related","views"]
docstr: Gets table metadata from the source system and SQLAlchemy inspector
docstr_tok: ["Gets","table","metadata","from","the","source","system","and","SQLAlchemy","inspector"]
code: def external_metadata_by_name(self, **kwargs: Any) -> FlaskResponse:
      """Gets table metadata from the source system and SQLAlchemy inspector"""
      try:
          params: ExternalMetadataParams = (
              ExternalMetadataSchema().load(kwargs.get("rison"))
          )
      except ValidationError as err:
          return json_error_response(str(err), status=400)
  
      datasource = SqlaTable.get_datasource_by_name(
          session=db.session,
          database_name=params["database_name"],
          schema=params["schema_name"],
          datasource_name=params["table_name"],
      )
      try:
          if datasource is not None:
              # Get columns from Superset metadata
              external_metadata = datasource.external_metadata()
          else:
              # Use the SQLAlchemy inspector to get columns
              database = (
                  db.session.query(Database)
                  .filter_by(database_name=params["database_name"])
                  .one()
              )
              external_metadata = get_physical_table_metadata(
                  database=database,
                  table_name=params["table_name"],
                  schema_name=params["schema_name"],
                  normalize_columns=params.get("normalize_columns")
                  or False,
              )
      except (NoResultFound, NoSuchTableError) as ex:
          raise DatasetNotFoundError() from ex
      return self.json_response(external_metadata)
  
imports: import json
  from collections import Counter
  from typing import Any
  from flask import redirect, request
  from flask_appbuilder import expose, permission_name
  from flask_appbuilder.api import rison
  from flask_appbuilder.security.decorators import has_access, has_access_api
  from flask_babel import _
  from marshmallow import ValidationError
  from sqlalchemy.exc import NoResultFound, NoSuchTableError
  from superset import db, event_logger, security_manager
  from superset.commands.dataset.exceptions import DatasetForbiddenError, DatasetNotFoundError
  from superset.commands.utils import populate_owners
  from superset.connectors.sqla.models import SqlaTable
  from superset.connectors.sqla.utils import get_physical_table_metadata
  from superset.daos.datasource import DatasourceDAO
  from superset.exceptions import SupersetException, SupersetSecurityException
  from superset.models.core import Database
  from superset.superset_typing import FlaskResponse
  from superset.utils.core import DatasourceType
  from superset.views.base import api, BaseSupersetView, deprecated, handle_api_exception, json_error_response
  from superset.views.datasource.schemas import ExternalMetadataParams, ExternalMetadataSchema, get_external_metadata_schema, SamplesPayloadSchema, SamplesRequestSchema
  from superset.views.datasource.utils import get_samples
  from superset.views.utils import sanitize_datasource_data
label_desc: Use image node_id 4 for calling the Datasource obj's underlying member method code with example usage: obj.external_metadata_by_name() and returns: self, json_error_response