Skip to content

[WIP] Related object tabs — PoC for discussion#434

Closed
Kani999 wants to merge 6 commits intonetboxlabs:mainfrom
Kani999:feature/related-object-tabs
Closed

[WIP] Related object tabs — PoC for discussion#434
Kani999 wants to merge 6 commits intonetboxlabs:mainfrom
Kani999:feature/related-object-tabs

Conversation

@Kani999
Copy link
Copy Markdown

@Kani999 Kani999 commented Mar 26, 2026

Summary

This is a Proof of Concept / Work in Progress — not ready for merge. The design and configuration approach need discussion before finalizing.

Integrates the standalone netbox-custom-objects-tab plugin into core netbox-custom-objects (ref: CESNET/netbox-custom-objects-tab#8).

Closes #26

What's implemented

  • Combined "Custom Objects" tab — automatically appears on detail pages of any NetBox object referenced by a Custom Object Type. Shows all linked custom objects with actions, tags, column config, and quick search. Always active, no configuration needed.
  • Per-COT typed tabs — opt-in dedicated tabs for specific Custom Object Types, with type-specific columns, per-field filters, and bulk actions. Configured via typed_tab_slugs in PLUGINS_CONFIG.
  • CO-to-CO support — custom objects referencing other custom objects also get tabs.
  • Plugin wiring (__init__.py), templates, and README documentation.

Open questions for discussion

  • Configuration approach: Currently typed tabs are enabled via typed_tab_slugs list in PLUGINS_CONFIG. Is this the right UX, or should this be a per-COT toggle in the admin UI?
  • Badge/count behavior: Badges use OR + distinct queries to avoid double-counting — are there performance concerns at scale?
  • Template/styling: Do the tab templates align with NetBox core conventions, or do they need adjustment?

Commits

  1. 1cac046 — Add related object tab views and templates
  2. ab29d56 — Wire tab registration into plugin and fix pre-existing view bugs
  3. 69c46a7 — Document related object tabs in README

Images

obrazek obrazek obrazek obrazek obrazek

@bctiemann
Copy link
Copy Markdown
Contributor

bctiemann commented Apr 20, 2026

@Kani999 We've enabled the CLA Assistant on this repo to ensure that all contributors sign the license agreement. However this was enabled after you posted your PR. Could I ask you please to push a trivial commit to this PR in order to trigger the CLA prompt? I'm hoping that will do it and won't require the PR to be wholly resubmitted (it should trigger on any synchronize event which will occur on a push). Thanks!

Jan Krupa added 3 commits April 21, 2026 08:40
- Add tab_views.py with combined and typed tab view factories
- Combined tab shows all linked custom objects with actions, tags, column config, quick search
- Typed tabs show per-COT filtered view with bulk actions and per-field filters
- Auto-discover referenced models from app registry (never call get_model during registration)
- Support CO-to-CO tabs (custom objects referencing other custom objects)
- Badge callables use OR + distinct to avoid double-counting
- Add combined_tab.html and typed_tab.html templates
- Add typed_tab_slugs to default_settings in PluginConfig
- Call register_all_tabs() in ready() before super().ready()
- Call inject_co_urls() and deduplicate_registry() after super().ready()
- Add model_view_tabs to customobject.html for CO-to-CO tab support
- Fix JournalEntryTable and ObjectChangeTable: remove unsupported user kwarg
- Fix journal/changelog views to pass self.tab instead of string literals
- Add Related Object Tabs section explaining combined and typed tabs
- Document typed_tab_slugs PLUGINS_CONFIG setting with example
- Note that restart is required after config changes
@Kani999 Kani999 force-pushed the feature/related-object-tabs branch from 69c46a7 to 237e766 Compare April 21, 2026 08:40
@Kani999
Copy link
Copy Markdown
Author

Kani999 commented Apr 21, 2026

@bctiemann Hi, I pushed a trivial commit but nothing seems to have happened — the CLA prompt didn't trigger. I suspect it might be because the PR is currently marked as Draft, so synchronize events may not fire (or at least not in the way the CLA Assistant expects).
I'll mark the PR as "Ready for review" to remove the draft status and then push another commit to try again. Will report back.
Thanks!

@Kani999 Kani999 marked this pull request as ready for review April 21, 2026 09:05
Jan Krupa added 3 commits April 21, 2026 10:49
- Replace _build_filterset_form with shared dynamic_forms.build_filterset_form_class
  (per prior PR netboxlabs#445 feedback / commit 26a39a5 invariant)
- Extract _build_link_q helper; removes copy-pasted Q loop from
  _count_for_type._badge and TypedTabView.get
- Drop CustomObjectType.objects.get(pk=cot_pk) refetch from typed-tab badge;
  use captured cot directly
- Narrow bare except Exception to (OperationalError, ProgrammingError) in
  _count_linked and _get_linked_objects
- prefetch_related('tags') in _get_linked_objects — kills per-row tag N+1
- Switch .restrict() try/except AttributeError to hasattr() guard; matches
  NetBox core pattern in netbox/views/generic/feature_views.py
- Fix stale module docstring: typed-tab opt-in is typed_tab_slugs in
  PLUGINS_CONFIG, not show_tab=True
Apply .restrict(user, 'view') to linked-CO querysets so users without
view permission on a referenced Custom Object model don't see its rows
rendered in the related-object tab bodies.

- _get_linked_objects now takes a user and restricts each per-model qs
  before filtering; the combined-tab view passes request.user.
- TypedTabView.get restricts dynamic_model.objects before the link-Q
  filter so the typed-tab body is also gated.

The combined-tab badge count can still include rows the user can't see;
the body render itself is now restricted.
Pass a permission string into ViewTab so NetBox's core tab template tag
skips the typed tab entirely for users without <app>.view_<model>
permission on the underlying Custom Object model. Previously the badge
count could include restricted rows while the body correctly hid them,
producing a badge-vs-empty-body mismatch.

The permission string is derived from the CO model resolved via
model_ct_map at registration time (cot.object_type_id -> model), so no
cot.get_model() call is introduced during tab registration.

The combined tab is left unguarded intentionally — it aggregates across
all CO types and is filtered per-row by the restrict() fix already in
place.
@bctiemann
Copy link
Copy Markdown
Contributor

@Kani999 I think the CLA may not trigger unless it's a brand-new PR. Could I please ask you to close this PR and open a new one (same description etc)? Thanks, and apologies for the extra work.

@Kani999
Copy link
Copy Markdown
Author

Kani999 commented Apr 24, 2026

Closing per @bctiemann's request above to trigger the CLA Assistant on a fresh PR. Reopening with the same description — link to follow.

@Kani999 Kani999 closed this Apr 24, 2026
@Kani999
Copy link
Copy Markdown
Author

Kani999 commented Apr 24, 2026

Reopened as #482

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto generation of tabs in other objects that lists related Custom Objects

2 participants