Closes #655: Surface context fields as attributes in global search results - #684
Closes #655: Surface context fields as attributes in global search results#684bctiemann wants to merge 2 commits into
Conversation
…sults Credit to Ben White (@biwhite), who identified this gap and opened #656 with the core insight this builds on: setting SearchIndex.display_attrs from a COT's context=True fields, so they surface as supplementary "attributes" on global search results the same way every other NetBox model's SearchIndex does. Two issues in the original approach, found while reviewing #656 for correctness: - It added a get_display_attrs classmethod to the dynamically generated SearchIndex class, but nothing in NetBox core or this plugin ever calls a method by that name. The actual renderer is CachedValue.display_attrs (netbox core's extras/models/search.py), which only reads the plain display_attrs tuple and does its own getattr()/get_<field>_display() dispatch per entry -- the added method was dead code. - Nothing prevents context=True from being set on a multiobject (real M2M) field. CachedValue.display_attrs's getattr()-based renderer has no per-type dispatch: for a real ManyToManyField that returns the RelatedManager instance itself, not its contents, so a multiobject context field would leak a broken object repr into search results instead of the related objects. Excluded multiobject context fields from display_attrs to avoid this. Polymorphic and coordinates fields need no equivalent exclusion -- they have no real backing column under the field's own name, so the existing `present` check (guarding against stub models generated with skip_object_fields=True) already filters them out. Also consolidated the two separate self.fields queries (one filtered by search_weight, one by context) into a single pass. Ben's own PR (#656) has this exact same root idea but is out of sync with main and failing CI for that reason, not because of a problem with his approach; opening this instead since I don't have push access to his fork to rebase it directly. Adds two regression tests: a context field appears in display_attrs, and a multiobject context field does not. Full plugin suite verified in a clean venv: 1173 tests, 0 failures/errors, 2 skipped.
|
@claude /review and limit response to actionable issues. |
|
Claude finished @bctiemann's task in 1m 52s —— View job Code Review
Two actionable issues, both in the tests: 1. Positive test has an incomplete assertion ( The test verifies self.assertIn("status", search_index.display_attrs)
self.assertNotIn("name", search_index.display_attrs) # add this2. No test for single-object ( There's a test that a The logic change itself ( |
- Strengthen the positive display_attrs test with assertNotIn("name", ...)
so a regression that dumps every field into display_attrs (not just
context=True ones) would be caught.
- Add test_register_search_index_includes_object_context_fields, the
missing other side of the TYPE_MULTIOBJECT exclusion: a TYPE_OBJECT
(single FK) context field must still be included, since getattr() on
it returns the related instance directly, not a manager.
|
Improved both tests as suggested. |
Closes: #655
Summary
Global search results for custom objects only ever showed a bare link to the object — none of its "context" fields appeared in the Attributes column the way core NetBox models do. This wires
CustomObjectType's dynamically generatedSearchIndexto surfacecontext=Truefields there, via NetBox core's existingSearchIndex.display_attrsmechanism.Credit
This builds directly on #656 by @biwhite, who filed #655 and correctly identified
display_attrsas the right hook. His PR is out of sync withmainand failing CI as a result — not because of any problem with his approach (see the comment thread on #655) — and I don't have push access to his fork to rebase it directly, so I'm opening this instead.What changed vs. #656
Reviewing #656 for correctness turned up two issues, both addressed here:
get_display_attrsclassmethod to the dynamically generatedSearchIndexclass, but nothing in NetBox core or this plugin ever calls a method by that name. The actual renderer isCachedValue.display_attrs(a core@property), which only reads the plaindisplay_attrstuple on the indexer class and does its owngetattr()/get_<field>_display()dispatch per entry — the added method was inert.context=Truefrom being set on amultiobject(real M2M) field.CachedValue.display_attrs'sgetattr()-based renderer has no per-type dispatch — for a realManyToManyFieldit returns theRelatedManagerinstance itself, not its contents, so a multiobject context field would leak a broken object repr into search results instead of the related objects. This PR excludes multiobject context fields fromdisplay_attrsto avoid that. Polymorphic and coordinates fields need no equivalent exclusion — they have no real backing column under the field's own name, so the existingpresent-set guard (already in place to protect stub models generated withskip_object_fields=True) filters them out automatically.Also consolidated the two separate
self.fieldsqueries (one filtered bysearch_weight, one bycontext) into a single pass.Testing
Adds two regression tests to
test_models.py:context=Truetext field appears in the generatedSearchIndex.display_attrscontext=Truemultiobject field does notFull plugin suite verified in a clean venv: 1173 tests, 0 failures/errors, 2 skipped.