cd /news/mlops/copy-a-w-b-saved-view-template-acros… · home topics mlops article
[ARTICLE · art-21876] src=gist.github.com pub= topic=mlops verified=true sentiment=· neutral

Copy a W&B saved-view template across projects

A developer created a Python script that copies a Weights & Biases saved-view template from one project's workspace to another, preserving all sections, panels, workspace settings, and runset configurations. The tool reads the source saved view via its URL, reconstructs an identical workspace in the destination project, and automatically bootstraps the destination project with a placeholder run if it doesn't already exist. The script uses the wandb-workspaces SDK and requires only the source URL, destination entity and project, and a new view name as inputs.

read4 min publishedMay 25, 2026

| """Copy a W&B saved-view template from one project's workspace to another. | | | Reads the source saved view via its URL (the ?nw=<id> query string identifies | | | the saved view) and reconstructs an identical Workspace in the destination | | | project — preserving sections/panels, workspace settings (smoothing, x-axis, | | | outliers, etc.), and runset settings (filters, grouping, ordering, pinned | | | columns, per-run colors/disabled state). | | | Setup: | | | pip install wandb wandb-workspaces | | | export WANDB_API_KEY=<your key> # or have it in ~/.netrc via wandb login | | | Usage: | | | python copy_savedview.py \ | |

| --source-url "https://wandb.ai/ENTITY/SRC_PROJECT?nw=abcd1234" \ | |
| --dest-entity ENTITY \ | |
| --dest-project DST_PROJECT \ | |
| --new-name "Copied template" | |

| Notes: | |

| - --source-url must be a saved-view URL (?nw=<id>). Personal/default | |
| workspace URLs (?nw=nwuser<username>) are not supported by the underlying | |

| wandb-workspaces SDK | | | - The destination project is auto-bootstrapped with a placeholder run if it | | | doesn't exist yet, because the GraphQL upsertView mutation requires the | | | project to exist server-side. | | | """ | | | import argparse | | | import copy | | | import os | | | import sys | | | import wandb | | | import wandb_workspaces.workspaces as ws | | | def ensure_project_exists(entity: str, project: str): | | | """Make sure the destination project exists server-side. | | | Workspace.save() upserts the view via GraphQL but returns 404 if the | | | target project hasn't actually been provisioned. api.create_project and | | | api.project() aren't always sufficient — initializing a tiny run is the | | | most reliable way to guarantee the project exists before we upsert a view | | | into it. | | | """ | | | api = wandb.Api() | | | try: | |

| api.project(name=project, entity=entity) | |
| print(f"Destination project {entity}/{project} already accessible.") | |

| return | | | except Exception: | | | pass | |

| print(f"Bootstrapping destination project {entity}/{project} with a placeholder run ...") | |
| run = wandb.init( | |

| entity=entity, | | | project=project, | | | name="_bootstrap_for_savedview_copy", | | | reinit=True, | | | tags=["_bootstrap"], | | | ) | |

| wandb.log({"_bootstrap": 1}) | |
| run.finish() | |

| def copy_workspace( | | | source_url: str, | | | dest_entity: str, | | | dest_project: str, | | | new_name: str, | | | ) -> ws.Workspace: | | | """Load the source saved view and rebuild it under the destination project.""" | |

| print(f" source saved view from: {source_url}") | |
| src = ws.Workspace.from_url(source_url) | |

| print( | |

| f"Loaded view '{src.name}' from {src.entity}/{src.project} with " | |
| f"{len(src.sections)} section(s), " | |
| f"{sum(len(s.panels) for s in src.sections)} panel(s)." | |

| ) | |

| print(f" filters: {src.runset_settings.filters!r}") | |
| print(f" groupby: {[g.name for g in src.runset_settings.groupby]}") | |
| print(f" order: {[(o.item.name, o.ascending) for o in src.runset_settings.order]}") | |
| print(f" pinned_columns: {src.runset_settings.pinned_columns}") | |

| # Deep-copy the structural pieces so mutating them on the new workspace | | | # doesn't disturb the loaded source object | |

| sections = copy.deepcopy(src.sections) | |
| settings = copy.deepcopy(src.settings) | |
| runset_settings = copy.deepcopy(src.runset_settings) | |
| dst = ws.Workspace( | |

| entity=dest_entity, | | | project=dest_project, | | | name=new_name, | | | sections=sections, | | | settings=settings, | | | runset_settings=runset_settings, | | | ) | |

| dst.save() | |
| print(f"\nCopied saved view URL:\n {dst.url}") | |

| return dst | |

| def main(): | |
| p = argparse.ArgumentParser( | |

| description="Copy a W&B saved-view template between projects." | | | ) | | | p.add_argument( | | | "--source-url", | | | required=True, | | | help='Saved-view URL, e.g. "https://wandb.ai/ENTITY/PROJECT?nw=abcd1234"', | | | ) | |

| p.add_argument("--dest-entity", required=True, help="Destination W&B entity (user or team)") | |
| p.add_argument("--dest-project", required=True, help="Destination W&B project") | |
| p.add_argument("--new-name", required=True, help="Name to display for the copied saved view") | |
| args = p.parse_args() | |
| api_key = os.environ.get("WANDB_API_KEY") | |

| if api_key: | | | wandb.login(key=api_key, relogin=True) | | | else: | | | if not wandb.login(): | | | sys.exit( | | | "ERROR: no W&B credentials found. Set WANDB_API_KEY or run " | | | "wandb login first." | | | ) | | | ensure_project_exists(args.dest_entity, args.dest_project) | | | copy_workspace( | | | source_url=args.source_url, | | | dest_entity=args.dest_entity, | | | dest_project=args.dest_project, | | | new_name=args.new_name, | | | ) | |

| if __name__ == "__main__": | |
| main() |
── more in #mlops 4 stories · sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/copy-a-w-b-saved-vie…] indexed:0 read:4min 2026-05-25 ·