docstr: Creates/updates and/or deletes a list of metrics, based on a
  list of Dict.
  
  - If a metric Dict has an `id` property then we update.
  - If a metric Dict does not have an `id` then we create a new metric.
  - If there are extra metrics on the metadata db that are not defined on the List
  then we delete.
docstr_tok: ["Creates\/updates","and\/or","deletes","a","list","of","metrics",",","based","on","a","list","of","Dict",".","-","If","a","metric","Dict","has","an","`","id","`","property","then","we","update",".","-","If","a","metric","Dict","does","not","have","an","`","id","`","then","we","create","a","new","metric",".","-","If","there","are","extra","metrics","on","the","metadata","db","that","are","not","defined","on","the","List","then","we","delete","."]
code: def update_metrics(
      cls,
      model: SqlaTable,
      property_metrics: list[dict[str, Any]],
      commit: bool = True,
  ) -> None:
      """
      Creates/updates and/or deletes a list of metrics, based on a
      list of Dict.
  
      - If a metric Dict has an `id` property then we update.
      - If a metric Dict does not have an `id` then we create a new metric.
      - If there are extra metrics on the metadata db that are not defined on the List
      then we delete.
      """
  
      metrics_by_id = {metric.id: metric for metric in model.metrics}
  
      property_metrics_by_id = {
          properties["id"]: properties
          for properties in property_metrics
          if "id" in properties
      }
  
      db.session.bulk_insert_mappings(
          SqlMetric,
          [
              {**properties, "table_id": model.id}
              for properties in property_metrics
              if not "id" in properties
          ],
      )
  
      db.session.bulk_update_mappings(
          SqlMetric,
          [
              {**metrics_by_id[properties["id"]].__dict__, **properties}
              for properties in property_metrics_by_id.values()
          ],
      )
  
      db.session.query(SqlMetric).filter(
          SqlMetric.id.in_(
              {metric.id for metric in model.metrics}
              - property_metrics_by_id.keys()
          )
      ).delete(synchronize_session="fetch")
  
      if commit:
          db.session.commit()
  
imports: from __future__ import annotations
  import logging
  from typing import Any
  from sqlalchemy.exc import SQLAlchemyError
  from superset.connectors.sqla.models import SqlaTable, SqlMetric, TableColumn
  from superset.daos.base import BaseDAO
  from superset.extensions import db
  from superset.models.core import Database
  from superset.models.dashboard import Dashboard
  from superset.models.slice import Slice
  from superset.utils.core import DatasourceType
  from superset.views.base import DatasourceFilter
label_desc: Use image node_id 12 for calling the DatasetDAO obj's underlying member method code with example usage: obj.update_metrics(cls, model, property_metrics, commit) without return types