English · Čeština · Dansk · Deutsch · Español · Français · Italiano · 日本語 · 한국어 · Latviešu · Nederlands · Polski · Português · Русский · Türkçe · Українська · 中文
NetBox Scope Switcher
Pick your scope once — tenant, region, site, tag, whatever you configure — and every list in NetBox narrows to it, with the filters cascading on each other.
netbox-scope-switcher adds one compact, configurable selector to the top
navigation bar. Choose values across any number of scope dimensions (grouped
however you like) and, as you move between sections (Devices, Prefixes, VLANs,
Sites, …), each list view is automatically scoped to your selection.
It generalizes
netbox-tenant-switcher:
tenant is just the default dimension.
Screenshots
Features
- Configurable, grouped dimensions. Scope by tenant, tenant group, region,
site group, site, location, tag, or any filter you declare in
PLUGINS_CONFIG— arranged under your own group headings. No code changes. - Cascading dependent filters. Selecting values in one dimension narrows the option lists of the others, in both directions: pick a Tenant Group and only its tenants remain; pick a Tenant and Regions/Sites/Racks shrink to where that tenant is present. Powered by NetBox's own filtersets (so tree descendants and every relationship are honoured), with no hard-coded model knowledge.
- Token-style control. One dropdown with a section per dimension: selected
values shown as removable chips, a
+ addpicker with instant search, anAnyplaceholder, aNoneoption (for objects with no value), per-dimension and global clear. Chips stay name-sorted. No external JS dependencies. - Localized. Dimension labels come straight from each model's NetBox
verbose_name, and the service words are translated via NetBox's catalog, so the panel follows the active UI language automatically. - Resizable & viewport-bounded panel. Drag the corner grip to resize (the size is remembered for the session); the panel never grows past the screen and scrolls internally.
- Honest URLs. By default the plugin issues a single redirect that adds the
filter params to the address bar, so links are shareable, NetBox's own filter
panel shows them, and Clear filters behaves predictably. A
silentmode is available. - Per-scope applicability. Each dimension is applied only on list views whose filterset actually supports it — a prefixes list may scope by tenant and tag but not region — computed by introspection, not hard-coding. The control dims when nothing applies to the current page.
- Combining semantics: several values within a dimension are OR-combined;
different dimensions are AND-combined;
Nonemaps to?param=null. - Permission-aware. Only values the user may view are listed and selectable, and scoping only ever narrows results — NetBox object permissions still apply on top.
- No models, no migrations. Selection lives in the session.
Compatibility
| Plugin version | NetBox version | Python |
|---|---|---|
| 1.0.x | 4.6 | 3.10 – 3.12 |
Developed and verified against NetBox 4.6, using only stable plugin APIs
(PluginTemplateExtension.navbar() / head(), plugin middleware). The CI matrix
(.github/workflows/ci.yml) additionally exercises 4.4 and 4.5.
Installation
source /opt/netbox/venv/bin/activate
pip install netbox-scope-switcher
Enable it in configuration.py:
PLUGINS = ["netbox_scope_switcher"]
Restart NetBox (sudo systemctl restart netbox netbox-rq). No migrations needed.
Docker (netbox-docker)
See deploy/ for a ready-to-use Dockerfile-Plugins,
docker-compose.override.yml, and plugins.py.
Configuration
Out of the box the plugin scopes by tenant only (matching
netbox-tenant-switcher). To configure dimensions and grouping, set
PLUGINS_CONFIG:
PLUGINS_CONFIG = {
"netbox_scope_switcher": {
"mode": "redirect", # or "silent"
"scopes": [
{"param": "tenant_group_id", "model": "tenancy.TenantGroup", "group": "Tenant"},
{"param": "tenant_id", "model": "tenancy.Tenant", "group": "Tenant"},
{"param": "region_id", "model": "dcim.Region", "group": "Location"},
{"param": "site_group_id", "model": "dcim.SiteGroup", "group": "Location"},
{"param": "site_id", "model": "dcim.Site", "group": "Location"},
{"param": "location_id", "model": "dcim.Location", "group": "Location"},
{"param": "tag", "model": "extras.Tag", "group": "Tag",
"value_field": "slug"},
],
}
}
PLUGINS_CONFIG keys
| Key | Type | Default | Description |
|---|---|---|---|
scopes |
list | tenant only | Ordered list of scope dimensions (below). |
mode |
string | redirect |
redirect (add params to the URL via a 302) or silent (rewrite the request in place). |
Each entry in scopes:
| Field | Required | Default | Description |
|---|---|---|---|
param |
yes | — | GET parameter of the filter to apply (e.g. tenant_id, tag). |
model |
yes | — | app_label.ModelName the dropdown values come from. |
group |
no | — | Group heading to list this dimension under (translated via NetBox). |
label |
no | model verbose name | Section heading. Omit it to follow the active UI language. |
value_field |
no | pk |
Value to put in the param: pk or slug (tags filter by slug). |
The configuration is validated at startup: an unknown model or field raises a
clear ImproperlyConfigured error on load, rather than a 500 at runtime.
How it works
- Navbar widget (
template_content.py) — aPluginTemplateExtensionrenders the control + a section per dimension vianavbar(); CSS/JS are injected once viahead(). Options are fetched from the values endpoint on open (so they can cascade); only the staticNoneoption is server-rendered. - Set endpoint (
views.py) —GET /plugins/scope-switcher/set/stores the chosen values per dimension in the session and redirects back.nextis validated against the current host (no open redirects); every value is checked against the user's view permission. - Values endpoint (
views.py) —GET /plugins/scope-switcher/values/<param>/?q=…&<other>=…returns a permission-scoped, cascaded, searchable JSON option list. - Cascade (
cascade.py) — narrows a dimension's options to those consistent with the current selection in the others. A forward pass filters a model by every other scope its own filterset supports (Site by Region, Rack by Location, …); a reverse pass handles the "upward" direction by pivoting through a bridge model that has a FK to the target and whose filterset does support the source (Site bridges Region↔Tenant, Tenant bridges Tenant Group↔Tenant, …). - Applicability (
applicability.py) — introspects the current list view's filterset (get_filters()/base_filters) to decide which configured params it supports; used to dim the control and to choose what to apply. - Middleware (
middleware.py) — forObjectListViewpages, appends one?param=<value>per selected value for each supported, not-already-present dimension.redirectmode issues a single 302;silentmode rewritesrequest.GET. API, GraphQL, and HTMX requests are skipped; existing query params (pagination, sort, per_page) are preserved.
Differences from netbox-tenant-switcher
netbox-tenant-switcher |
netbox-scope-switcher |
|
|---|---|---|
| Dimensions | tenant only (hard-coded) | any, configurable and grouped |
| Cascade | — | filters narrow each other, both directions |
| Control | one tenant field | one control, a section + chips per dimension |
| Localization | — | labels/service words follow the UI language |
| URL handling | redirect | redirect (default) or silent |
| Session state | list of tenant ids | {param: [values]} dict |
Migrating
pip uninstall netbox-tenant-switcherand remove it fromPLUGINS.pip install netbox-scope-switcher, add"netbox_scope_switcher"toPLUGINS.- Do nothing else for identical behaviour — the default config is the tenant
scope. Add more dimensions to
PLUGINS_CONFIGwhen you want them.
Sessions do not carry over (different key/shape); users simply re-pick their scope once.
Testing
Tests live in netbox_scope_switcher/tests/ and run inside a NetBox checkout:
python netbox/netbox/manage.py test netbox_scope_switcher -v 2
See .github/workflows/ci.yml for the full CI setup.
License
Metadata
Release files for netbox-scope-switcher 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| netbox_scope_switcher-1.0.2.tar.gz | 34.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| netbox_scope_switcher-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 70.6 kB
Release files / netbox_scope_switcher-1.0.2.tar.gz
| Download URL | netbox_scope_switcher-1.0.2.tar.gz |
|---|---|
| Size | 34.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
25b88fab2c5250a45410699ea30bfbf4b83a7a2ec9613319f87cdbc6dc32a92c
|
|
BLAKE2b-256 checksum How to use checksums |
8747c11aa2c103d15f9eea81d36524a9e3ccf5cc2063d6d594006339b95599d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / netbox_scope_switcher-1.0.2-py3-none-any.whl
| Download URL | netbox_scope_switcher-1.0.2-py3-none-any.whl |
|---|---|
| Size | 36.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
93dfeeacb18531d9188d5093190f47735afeb26e7b27b1caffefb70fee6fee4b
|
|
BLAKE2b-256 checksum How to use checksums |
830b4983b5a5cc9d5af93da303908463be6b1f9b8a4492d1223ba0e475ba6a3a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency log