Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,57 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.0.0] - 2026-09-16

### Removed

- **Combined "Custom Objects" tab and its settings** (`combined_models`,
`combined_label`, `combined_weight`). `netbox-custom-objects` 0.7.0 ships the
same tab natively (`netbox_custom_objects/related_tabs/`, weight 2000, no
configuration, live on every referenced object and on Custom Object pages).
Our copy registered under the same view name and had silently become a
no-op next to it. Leftover `combined_*` keys in `PLUGINS_CONFIG` now only
produce a startup warning.
- **Template override of `netbox_custom_objects/customobject.html`** and the
`template_override` module. Upstream's 0.7.0 template renders registered
model-view tabs itself via `{% plugin_extra_tabs %}`, so the typed tabs
reach Custom Object detail pages without shadowing the template.
- **`custom_object_tab_tags` templatetag library.** Upstream 0.7.0 ships a
library with the *same module name*; Django lets the later app in
`INSTALLED_APPS` win, so ours shadowed upstream's and broke
`{% custom_objects_tab_link %}` on every Custom Object page. Deleted.
- `TODO.md` (combined-tab backlog only).

### Changed

- **Requires `netbox-custom-objects` ≥ 0.7.0.** `PluginConfig.ready()` probes
for the upstream `related_tabs` package and raises `ImproperlyConfigured`
with an upgrade hint when it is missing. Installs on 0.6.x must stay on
plugin 2.6.x.
- **Typed tabs on Custom Object pages are served by a slug dispatcher.** The
shared `<type-slug>/<pk>/custom-objects-<slug>/` route now resolves the host
Custom Object Type from the URL per request and dispatches to the view
registered for exactly that model, instead of being bound to whichever host
model registered first.
- `typed_models` remains opt-in (default `[]`); with the combined tab gone the
plugin is inactive until it is set.

### Fixed

- **Wrong host object / never-active tab on Custom Object pages** when one
Custom Object Type referenced several other Custom Object Types. The shared
route was bound to the first host model's view, so pages of the other host
types loaded the object with the same pk from the wrong table, and the
`ViewTab` placed in the context never matched the page's own registry entry.
Fixed by the dispatcher above; the `(label, weight)` comparison workaround
from 2.6.0 is no longer needed.

### Upgrade notes

1. `pip install -U 'netboxlabs-netbox-custom-objects>=0.7.0' netbox-custom-objects-tab`
2. Remove `combined_models` / `combined_label` / `combined_weight` from `PLUGINS_CONFIG`.
3. Restart NetBox.

## [2.6.1] - 2026-09-14

### Fixed
Expand Down
303 changes: 115 additions & 188 deletions CLAUDE.md

Large diffs are not rendered by default.

350 changes: 86 additions & 264 deletions README.md

Large diffs are not rendered by default.

20 changes: 0 additions & 20 deletions TODO.md

This file was deleted.

76 changes: 37 additions & 39 deletions netbox_custom_objects_tab/__init__.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,73 @@
import logging
from importlib.metadata import version

from netbox.plugins import PluginConfig

logger = logging.getLogger("netbox_custom_objects_tab")

_REMOVED_SETTINGS = ("combined_models", "combined_label", "combined_weight")


class NetBoxCustomObjectsTabConfig(PluginConfig):
name = "netbox_custom_objects_tab"
verbose_name = "Custom Objects Tab"
description = 'Adds a "Custom Objects" tab to NetBox object detail pages'
verbose_name = "Custom Object Type Tabs"
description = "Adds one tab per Custom Object Type to NetBox object detail pages"
version = version("netbox-custom-objects-tab")
author = "Jan Krupa"
author_email = "jan.krupa@cesnet.cz"
base_url = "custom-objects-tab"
min_version = "4.5.2"
max_version = "4.7.99"
default_settings = {
# Per-type tabs: each Custom Object Type gets its own tab (opt-in, empty by default).
# Models that get one tab per Custom Object Type referencing them (opt-in).
# Accepts "app_label.model" or "app_label.*"; "netbox_custom_objects.*" enables
# tabs on Custom Object detail pages themselves (CO -> CO references).
"typed_models": [],
# Combined tab: single "Custom Objects" tab showing all types (current behavior).
"combined_models": [
"dcim.*",
"ipam.*",
"virtualization.*",
"tenancy.*",
],
# Label shown on the combined tab; override in PLUGINS_CONFIG.
"combined_label": "Custom Objects",
# Tab sort weight for the combined tab.
"combined_weight": 2000,
# Tab sort weight for all typed tabs.
# Tab sort weight shared by all typed tabs. netbox-custom-objects' own combined
# "Custom Objects" tab sits at 2000, so typed tabs render right after it.
"typed_weight": 2100,
}

def ready(self):
super().ready()

# Hard gate: require netbox-custom-objects >= 0.6.0. We probe behaviour
# (the `coordinates` field type added in 0.6.0) rather than parsing a
# version string, because forks and pre-release tags can carry any
# version label but either have or lack the feature we actually use.
# Our customobject.html override is a copy of the 0.6.0 stock template
# (Contacts/Config Context tabs, coordinates rendering, owner header),
# which reverses URLs that don't exist on 0.5.x — hence the hard floor.
# Raising ImproperlyConfigured here aborts NetBox startup with a clean,
# named error in the logs — preferable to letting a half-loaded plugin
# NoReverseMatch mid-request.
from django.apps import apps
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured

# netbox_custom_objects must actually be loaded, not merely installed.
# NetBox skips a plugin whose max_version is below the running release
# (netbox-custom-objects 0.6.0 caps at 4.6.99, so NetBox 4.7 drops it
# with only a warning); importing its models then fails with an opaque
# "isn't in INSTALLED_APPS" RuntimeError at startup.
# netbox_custom_objects must actually be loaded, not merely installed: NetBox
# skips a plugin whose max_version is below the running release, and importing
# its models then fails with an opaque "isn't in INSTALLED_APPS" RuntimeError.
if not apps.is_installed("netbox_custom_objects"):
raise ImproperlyConfigured(
"netbox-custom-objects-tab requires the netbox_custom_objects plugin to be loaded. "
"On NetBox 4.7+ that needs netbox-custom-objects>=0.6.1 (0.6.0 declares max_version 4.6.99 "
"and is skipped by NetBox). Upgrade with: pip install -U 'netbox-custom-objects>=0.6.1'"
"Install/upgrade with: pip install -U 'netboxlabs-netbox-custom-objects>=0.7.0'"
)

from netbox_custom_objects.choices import CustomObjectFieldTypeChoices

if not hasattr(CustomObjectFieldTypeChoices, "TYPE_COORDINATES"):
# Hard gate: netbox-custom-objects >= 0.7.0. We probe for the related_tabs
# package (new in 0.7.0) rather than parsing a version string, because forks
# and pre-release tags can carry any label. 0.7.0 matters because (a) it ships
# the combined "Custom Objects" tab this plugin used to provide, and (b) its
# customobject.html calls {% plugin_extra_tabs %}, which is how our typed tabs
# reach Custom Object detail pages without a template override.
try:
import netbox_custom_objects.related_tabs.registry # noqa: F401
except ImportError as exc:
raise ImproperlyConfigured(
"netbox-custom-objects-tab 2.5+ requires netbox-custom-objects>=0.6.0. "
"Upgrade with: pip install -U 'netbox-custom-objects>=0.6.0'"
"netbox-custom-objects-tab 3.0+ requires netbox-custom-objects>=0.7.0. "
"Upgrade with: pip install -U 'netboxlabs-netbox-custom-objects>=0.7.0'"
) from exc

stale = [k for k in _REMOVED_SETTINGS if k in settings.PLUGINS_CONFIG.get(self.name, {})]
if stale:
logger.warning(
"netbox_custom_objects_tab: %s ignored — the combined 'Custom Objects' tab is built into "
"netbox-custom-objects >= 0.7.0; remove these keys from PLUGINS_CONFIG",
", ".join(stale),
)

from . import template_override, views
from . import views

template_override.install()
views.register_tabs()


Expand Down
33 changes: 0 additions & 33 deletions netbox_custom_objects_tab/template_override.py

This file was deleted.

Loading
Loading