Skip to content

Releases: CESNET/netbox-custom-objects-tab

v2.3.0 — Add-button on Typed tabs

12 May 11:17
2ba90eb

Choose a tag to compare

[2.3.0] - 2026-05-12

Added

  • Add button on Typed tabs (#9) —
    each Typed tab now shows an "Add Type" button in the bottom toolbar
    (alongside Bulk Edit and Bulk Delete) that opens the native
    customobject_add view with the reverse-reference field pre-filled to the
    parent object's PK and return_url set back to the tab. After saving, the
    user lands back on the same tab, with any active filters preserved. When a
    Custom Object Type has multiple fields referencing the same parent model
    (e.g. primary_device and backup_device both → Device), the button
    becomes a split-dropdown listing each field. The button is hidden for
    users without add_customobject permission.

Fixed

  • Typed-tab URL registration: typed-tab views are now registered
    synchronously inside AppConfig.ready() instead of from a request_started
    signal handler. The earlier deferral (commit 5bf09c3, PR #4) silenced
    some startup warnings but broke typed-tab routing entirely — NetBox's
    get_model_urls() snapshots registry['views'] when each model's
    urls.py is first imported, so any view added afterward has no URL
    pattern. Combined tabs were unaffected because they were already
    synchronous; typed tabs were unreachable on every deployment with
    typed_models configured. The OperationalError/ProgrammingError
    safety net inside register_typed_tabs still covers the
    manage.py migrate / fresh-DB case.
  • Typed-tab badge no longer over-counts rows that match the parent via
    multiple fields. _count_for_type previously summed per-field counts
    with no deduplication, so a Custom Object Type with several fields
    pointing to the same parent model (e.g. primary_device +
    backup_device + affected_devices all → dcim.device) reported a
    badge number larger than the actual table row count whenever a row
    matched the parent via more than one field. Now uses the same
    Q-OR-Q + .distinct() pattern as the table queryset, so the badge and
    the table always agree. Bonus: one SQL query per tab badge instead of N
    (one per Device-pointing field). Bug existed since the typed-tab
    feature was introduced in 2.0.0; only became visible with multi-FK or
    M2M field combinations.
  • Typed-tab Bulk Edit / Bulk Delete buttons are now permission-gated
    against netbox_custom_objects.change_customobject /
    delete_customobject respectively, matching the gating pattern the
    Add button uses. Previously these buttons rendered unconditionally on
    Typed tabs; clicks were rejected server-side by NetBox's
    customobject_bulk_* views but the unguarded UI render was confusing
    for non-superusers. Per-button guards (rather than gating the whole
    toolbar on change AND delete) so a user with only change perm
    sees Bulk Edit but not Bulk Delete, and vice versa. Surfaced by the
    2.3.0 smoke test with non-admin test users.

Known Issues

  • Upstream netbox_custom_objects bug surfaced by the new Add button:
    deleting a custom object immediately after creating it via the
    2.3.0 Add button (Create → row dropdown → Delete in the typed tab list)
    raises ValueError: Cannot query "X": Must be "Table<N>Model" instance.
    from CustomObjectDeleteView._get_dependent_objects (upstream
    netbox_custom_objects/views.py:977). The error fires only on the
    first delete GET in that flow; refreshing the list page before
    clicking Delete works around it, and Bulk Delete (different code path)
    is unaffected. Root cause is dynamic-model class identity drift across
    the Create → Delete request boundary in upstream code (the dynamic
    model class registry rebuilds during Create, but the immediately-
    following Delete request still holds a reference to the previous class
    in some scope). Tracked here as a documentation-only release note since
    the fix needs to land in netbox_custom_objects, not in this plugin.
    See README "Known Issues" section for user-facing workarounds.

v2.2.0

11 May 09:33
379f1d7

Choose a tag to compare

Changed

  • Widen supported NetBox range to 4.5.0 – 4.6.99 (max_version bumped from 4.5.99 to 4.6.99). No code or template changes were required: every NetBox API the plugin depends on — ViewTab, register_model_view, htmx_partial, EnhancedPaginator, get_paginate_count, BaseTable, NetBoxModelFilterSetForm, SavedFiltersMixin, TagFilterField, CustomFieldTypeChoices, CustomFieldUIVisibleChoices, and the registry['views'] shape — is unchanged in NetBox 4.6 (verified against the v4.6.0 upstream tag). The 4.6 deprecations of registry['models'] and legacy actions = {...} view dicts do not affect this plugin.
  • On NetBox 4.6, the upstream netbox_custom_objects plugin ≥ 0.5.0 is recommended (its max_version covers 4.6.99). The CO detail-page template override remains necessary — customobject.html in upstream v0.5.0 still hardcodes its {% block tabs %} without {% model_view_tabs object %}.

Full Changelog: v2.1.1...v2.2.0

v2.1.1

18 Mar 09:14
ed27d11

Choose a tag to compare

netbox-custom-objects-tab v2.1.1

Highlights

Deferred DB queries — Typed-tab registration no longer queries the database during AppConfig.ready(), eliminating startup warnings from Django and netbox_branching.

What's Fixed

Fixed

  • Deferred typed-tab initialization — DB-dependent work (typed-tab registration, CO URL injection, registry deduplication) is now deferred to the first HTTP request via Django's request_started signal. This prevents:
    • Django warning: "Accessing the database during app initialization is discouraged"
    • netbox_branching warning: "Routing database query before branching support is initialized"
  • Combined tabs continue to register immediately during ready() (no DB access needed).
  • Thread-safe one-shot handler ensures deferred init runs exactly once, then disconnects itself.

Fixes #4

How It Works

register_tabs() is now two-phase:

  1. Phase 1 (ready()) — registers combined tabs (factory pattern, no DB queries)
  2. Phase 2 (first request) — a one-shot request_started signal handler registers typed tabs, injects CO URL patterns, and deduplicates the view registry

Compatibility

netbox-custom-objects-tab NetBox netbox_custom_objects
2.1.x 4.5.0–4.5.99 ≥ 0.4.6
2.0.x 4.5.0–4.5.99 ≥ 0.4.6

Links

v2.1.0

17 Mar 11:28
4c2c049

Choose a tag to compare

netbox-custom-objects-tab v2.1.0

Highlights

CO→CO tab support — Custom Object detail pages can now show tabs listing other Custom Objects that reference them. If Custom Object Type A has a FK or M2M field pointing to Type B, navigating to a Type B instance will display a tab of all related Type A instances.

What's New

Added

  • netbox_custom_objects.* wildcard — use in combined_models or typed_models to enable tabs on Custom Object detail pages themselves (CO→CO relationships).
  • Template override mechanism (template_override.py) — prepends plugin templates directory to Django's filesystem loader so the overridden customobject.html (with {% model_view_tabs object %}) is found before the original.
  • URL injection for CO tab views — _inject_co_urls() appends URL patterns to netbox_custom_objects.urls at startup, enabling URL reversal for dynamically registered tabs.

Fixed

  • Tab views now accept **kwargs in get(), accommodating the extra custom_object_type URL keyword argument on Custom Object detail URLs.
  • base_template for Custom Object instances correctly resolves to netbox_custom_objects/customobject.html instead of a nonexistent per-model template.
  • Added missing preferences key to error context in typed tab view.

Configuration Example

PLUGINS_CONFIG = {
    "netbox_custom_objects_tab": {
        "combined_models": ["dcim.*", "ipam.*", "netbox_custom_objects.*"],
        "typed_models": ["dcim.*", "netbox_custom_objects.*"],
    }
}

A NetBox restart is required whenever a new Custom Object Type is added.

Compatibility

netbox-custom-objects-tab NetBox netbox_custom_objects
2.1.x 4.5.0–4.5.99 ≥ 0.4.6
2.0.x 4.5.0–4.5.99 ≥ 0.4.6

Links

v2.0.2

06 Mar 12:24
421df7a

Choose a tag to compare

What's Changed

Fixed

  • TypeError crash on typed tab — removed user= keyword argument from CustomObjectTable instantiation. django_tables2.Table.__init__ does not accept this kwarg, causing a crash on NetBox 4.5.4-Docker with netbox_custom_objects 0.4.6. The argument was redundant — table.configure(request) already applies per-user preferences.

Changed

  • Single-source version — version is now defined only in pyproject.toml and read at runtime via importlib.metadata.version(), eliminating the duplicate string in __init__.py.

Compatibility

  • NetBox: 4.5.0 – 4.5.99
  • netbox_custom_objects: 0.4.6+
  • Python: 3.12 / 3.13 / 3.14

v2.0.1

25 Feb 17:17

Choose a tag to compare

What's Changed

Added

  • Typed tabs (per-type) — each Custom Object Type gets its own tab with a full-featured list view: type-specific columns, filterset sidebar, bulk edit/delete, configure table, and HTMX pagination.
  • typed_models and typed_weight config settings.
  • Third-party plugin model support for both tab modes.

Changed

  • Renamed models config to combined_models; label to combined_label; weight to combined_weight.
  • Refactored views from single views.py to views/ package (__init__.py, combined.py, typed.py).
  • Templates reorganized into combined/ and typed/ subdirectories.

Fixed

  • Handle missing database during startup — register_typed_tabs() now catches OperationalError and ProgrammingError so NetBox can start even when the database is unavailable or migrations haven't run yet.
  • Bulk action return URL in typed tabs — uses query parameter ?return_url= on formaction for reliable redirect.

Full Changelog: https://github.com/CESNET/netbox-custom-objects-tab/blob/v2.0.1/CHANGELOG.md

v2.0.1b1

25 Feb 15:46

Choose a tag to compare

v2.0.1b1 Pre-release
Pre-release

What's New

Typed tabs (per-type) — each Custom Object Type gets its own tab with a full-featured list view: type-specific columns, filterset sidebar, bulk edit/delete, configure table, and HTMX pagination.

Added

  • typed_models and typed_weight config settings
  • Third-party plugin model support for both tab modes

Changed

  • Renamed config: modelscombined_models, labelcombined_label, weightcombined_weight
  • Refactored views into views/ package; templates into combined/ and typed/ subdirectories

Fixed

  • Bulk action return URL in typed tabs

See CHANGELOG.md for full details.

v1.0.1

24 Feb 19:07

Choose a tag to compare

Bug Fixes

Templates missing from installed package (#packaging)

When installing netbox-custom-objects-tab from PyPI or a pre-built wheel, the HTML
templates were not included in the package, causing a TemplateDoesNotExist error on
every tab page load. This particularly affected Docker-based NetBox deployments.

Root cause: setuptools does not include non-Python files by default. The
[tool.setuptools.package-data] directive was missing from pyproject.toml, and no
MANIFEST.in existed to cover source distributions.

Fix: Added MANIFEST.in and [tool.setuptools.package-data] in pyproject.toml
so all HTML templates under netbox_custom_objects_tab/templates/ are bundled into
both wheel and sdist artifacts.

Upgrade Notes

If you installed 1.0.0 from PyPI, upgrade with:

pip install --upgrade netbox-custom-objects-tab

No migrations, no configuration changes required.

Full Changelog: v1.0.0...v1.0.1

v1.0.0

24 Feb 18:04

Choose a tag to compare

What's New

First stable release of netbox-custom-objects-tab — a NetBox 4.5.x plugin that adds
a Custom Objects tab to standard object detail pages, showing any Custom Object
instances from the netbox_custom_objects plugin that reference the viewed object via
OBJECT or MULTIOBJECT fields.

Added

  • Custom Objects tab on NetBox object detail pages (Device, Site, Rack, and any
    configured model), showing Custom Object instances that reference the viewed object
    via OBJECT or MULTIOBJECT typed fields.
  • Pagination using NetBox's EnhancedPaginator; respects the user's personal
    per-page preference and the ?per_page=N URL parameter.
  • Text search (?q=) filtering results by Custom Object instance display name,
    Custom Object Type name, and field label.
  • Type filter dropdown (?type=<slug>) to narrow results to a single Custom Object
    Type, populated dynamically from types present in the current result set.
  • Tag filter dropdown (?tag=<slug>) to narrow results to objects with a specific
    tag. Tag data is pre-fetched in bulk — no N+1 query cost.
  • Column sorting — clicking Type, Object, or Field column headers sorts
    the table in-memory; a second click toggles direction.
  • Value column — shows the actual field value: a link for OBJECT fields, or
    comma-separated links (truncated at 3) for MULTIOBJECT fields.
  • Tags column — colored tag badges per row; when none.
  • Permission-gated action buttons — Edit (requires change) and Delete (requires
    delete) buttons per row. Users without permissions see no action buttons.
  • HTMX partial updates — pagination, sorting, search, type-dropdown, and
    tag-dropdown changes swap only the table zone in-place without a full page reload.
    URL is updated via pushState so links remain shareable and the back button works.
  • Configure Table — a "Configure Table" button lets authenticated users show, hide,
    and reorder columns (Type, Object, Value, Field, Tags). Preferences are persisted
    per-user in UserConfig. The Actions column is always visible.
  • Efficient badge counts — the tab badge on detail pages is computed with COUNT(*)
    queries only; full object rows are loaded only when the tab itself is opened.
  • Wildcard model registrationmodels config accepts app_label.* wildcards
    (e.g. dcim.*, ipam.*).
  • Third-party plugin model support — any installed Django app can be listed in models.
  • Default models: ['dcim.*', 'ipam.*', 'virtualization.*', 'tenancy.*', 'contacts.*'].
  • Tab is hidden automatically (hide_if_empty=True) when no custom objects reference the
    viewed object.
  • Configurable tab label and weight via PLUGINS_CONFIG.

Fixed

  • Edit/Delete return URL — after saving an edit or confirming a deletion, NetBox
    redirects back to the Custom Objects tab (preserving active filters) instead of the
    Custom Object list page.

Requirements

  • NetBox 4.5.0 – 4.5.99
  • netbox_custom_objects plugin installed and configured

Installation

pip install netbox-custom-objects-tab==1.0.0

See the README for
full setup instructions.