I have a dashboard that I want to use as a template for new dashboards, I figured a workflow like below should do the trick

  • get template dashboard data as json
  • create new empty dashboard
  • for each widget in the dashboard, recreate it’s query
  • while recreating the query replace the table name from the SQL query with new table name
  • name the query according to username and query name
  • run newly created query to refresh it’s results
  • update the query filed in the visualization field in the widget to the new created query
  • if a widget is encountered where it’s query has been already re-created use that (using query name for matching), otherwise create a new one
  • update new empty dashboard with new data

currently, the queries are correctly created with updated SQL table and results, however, I get an error when updating the dashboard saying conflict for URL with no other information.

what does the error mean? and is there a simpler way to do this?

here’s the code for the function

def create_dashboard(name: str, table_map: dict) -> str:
    """
    Creates a new dashboard with `name` from template
    replacing old tables with new tables according to `table_map`.
    """
    username = table_map[list(table_map.keys())[0]].split("_")[0]
    template = "churn-template"
    template_id = get_dashboard_id(template)
    dash_data = rd.get_dashboard(template_id)
    res = rd.create_dashboard(name)
    new_id = res["id"]
    for widget in dash_data["widgets"]:
        widget["dashboard_id"] = new_id
        query = widget["visualization"]["query"]
        new_q_id = get_query_id(f"{username}_{query['name']}")
        if new_q_id:
            widget["visualization"]["query"] = rd.get_query(new_q_id)
            continue
        widget["visualization"]["query"] = duplicate_query_table(query, table_map)
    # print(dash_data)
    rd.update_dashboard(new_id, dash_data)
    publish_dashboard(name)
    return share_dashboard(name)
1 Like

I am here to answer my own question, once more
the problem was my understanding of redash abstractions, what needed to be re-created was the widgets themselves with options from the template to preserve position, here’s an updated code that works

def create_dashboard(name: str, table_map: dict) -> str:
    username = table_map[list(table_map.keys())[0]].split("_")[0]
    template = "churn-template"
    template_id = get_dashboard_id(template)
    old_data = rd.get_dashboard(template_id)
    new_id = rd.create_dashboard(name)["id"]
    for widget in old_data["widgets"]:
        options = widget["options"]
        query = widget["visualization"]["query"]
        old_vis = widget["visualization"]
        new_q_id = get_query_id(f"{username}_{query['name']}")
        if new_q_id:
            rd.create_widget(
                dashboard_id=new_id,
                visualization_id=get_vis_id_query(
                    old_vis["name"], rd.get_query(new_q_id)
                ),
                text="",
                options=options,
            )
            continue
        new_q = duplicate_query_table(query, table_map)
        rd.create_widget(
            dashboard_id=new_id,
            visualization_id=get_vis_id_query(
                old_vis["name"], rd.get_query(new_q["id"])
            ),
            text="",
            options=options,
        )

    publish_dashboard(name)
    return share_dashboard(name)

Thanks for posting your solution!

For reference, here is the official redash-toolbelt script for cloning dashboards. It behaves similarly, although it interacts through the API: redash-toolbelt/clone_dashboard_and_queries.py at master · getredash/redash-toolbelt · GitHub