Closes #19731: Add ModuleBayType to restrict which module types can be installed into a module bay#22648
Closes #19731: Add ModuleBayType to restrict which module types can be installed into a module bay#22648bctiemann wants to merge 9 commits into
Conversation
…nstall into a bay Introduces a new ModuleBayType model — a color-coded, manufacturer-scoped type label — that can be assigned to ModuleBays, ModuleBayTemplates, and ModuleTypes via M2M relations. When both a bay and a module type carry at least one bay-type tag, installation is permitted only if their tag sets share a common member; an empty set on either side means unconstrained. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix N+1 queries: use .all() set comprehensions in is_bay_compatible and is_module_compatible so Django prefetch cache is honoured - Delegate clean() compatibility check to is_bay_compatible property - Use getattr() instead of hasattr() for installed_module access - Replace unbounded RelatedObjectListAttr with paginated ObjectsTablePanel in ModuleBayType detail view - Fix BayTypeIncompatibilityPanel to use isinstance() dispatch and pass explicit is_module_view context variable to template - Add slug to ModuleBayTypeFilterSet search - Add _source_template comment in ModuleBayTemplate.instantiate() - Update query count baselines after prefetch fix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New docs/models/dcim/modulebaytype.md describing the model and the opt-in allow-list compatibility semantics - Update modulebay.md, moduletype.md with bay_types field docs - Update module.md with is_bay_compatible API flag and compatibility note - Update modulebaytemplate.md to note that bay types propagate on instantiation - Add ModuleBayType to mkdocs.yml nav Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@claude /review and report only on areas of potential concern. |
|
Claude finished @bctiemann's task in 4m 57s —— View job Code Review: ModuleBayType FeatureThe implementation is well-structured and handles the three installation paths with an appropriate blocking vs. warning strategy. Below are the areas of potential concern. 1. N+1 Queries in UI Detail Views
The
The detail views should prefetch these chains so 2.
|
- Add prefetch_related for module_bay_types chains to ModuleView and ModuleBayView detail views so BayTypeIncompatibilityPanel.should_render() hits the cache instead of issuing extra queries - Add module_bay_types to ModuleBayViewSet explicit prefetch for parity with the installed_module chain (dynamic serializer prefetch already handles it; this makes the intent explicit) - Add .distinct() to get_incompatible_modules() constrained_bay_pks subquery to prevent M2M join duplicates - Add comment to post() overrides explaining the response-detection logic and why _quickadd is excluded without an explicit guard - Document GraphQL ModuleBayType / ModuleBayTypeType naming collision in modulebaytype.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
All items are addressed except 4 which is out of scope. |
Add add_module_bay_types / remove_module_bay_types fields to both ModuleTypeBulkEditForm and ModuleBayBulkEditForm so bay type constraints can be managed in bulk without overwriting the full M2M set. Override post_save_operations in the corresponding bulk edit views to apply the add/remove operations and accumulate a count of newly- incompatible objects. A warning message is emitted after a successful save if any incompatibilities were introduced. The incompatibility check is guarded so it only runs when bay types were actually changed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both forms included 'enabled' via form_from_model(), which generates a plain BooleanField (always True or False). This caused the field to be applied to every selected object even when the user did not intend to change it. Replace with an explicit NullBooleanField + BulkEditNullBooleanSelect, matching the pattern used by ConsolePort, PowerPort, Interface, and other component bulk edit forms. The null/blank state now means "leave unchanged". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Bulk editing has now been implemented, including the retroactive incompatibility warning called out in item 4. I have also fixed a pre-existing bug where |
|
@claude /review just the changes since the last review and previously raised items. |
|
Claude finished @bctiemann's task in 4m 55s —— View job Code Review: Changes Since Last Review
Previous Review Items — All Addressed
New Changes:
|
Replace single-tab TabbedGroups wrappers in ModuleTypeBulkEditForm and ModuleBayBulkEditForm with plain FieldSet — a single tab has no toggle and violates TabbedGroups contract of two or more tabs. Add comment to both bulk edit post_save_operations explaining that the incompatibility count reflects current state, not purely the delta from this edit, so it may over-warn when pre-existing incompatibilities exist. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Fixed the two remaining items from the last review. Changed to a plain |
jnovinger
left a comment
There was a problem hiding this comment.
Looks like some merge conflicts creeped in.
Resolves conflicts with the feature branch's ModuleType.end_of_life addition (both branches added fields to ModuleType's field lists. Renumbers 0242_modulebaytype.py to 0243_modulebaytype.py to avoid colliding with feature's 0242_add_devicetype_end_of_life.py, and updates its dcim dependency accordingly. EOF )

Summary
Closes: #19731
Introduces ModuleBayType — a color-coded, manufacturer-scoped type label that can be assigned to
ModuleBays,ModuleBayTemplates, andModuleTypes via M2M relations. When a bay and the module type being installed both carry at least one bay-type tag, installation is permitted only if their tag sets share at least one common member. An empty set on either side means unconstrained (backward-compatible default).What's new
ModuleBayTypemodel (dcim) — hasname,slug,manufacturer(optional), andcolorfields; fullPrimaryModelfeature set (tags, custom fields, changelog, etc.)ModuleBay,ModuleBayTemplate, andModuleTypeDesign decisions: blocking vs. non-blocking validation
There are three distinct paths by which a module can end up in an incompatible bay, and we treat them differently by design:
/api/dcim/modules/or UI)Module.clean())Module.clean())ModuleTypeorModuleBay's bay types so that an already-installed module is no longer compatibleThe retroactive incompatibility cases are surfaced via:
Module.is_bay_compatible/ModuleBay.is_module_compatible— model properties that evaluate compatibility on demand with no extra DB hits when the relevant M2M fields are prefetched.BayTypeIncompatibilityPanel— a Bootstrap warning card rendered at the top of the Module and ModuleBay detail views when incompatibility is detected.ModuleTypeEditViewandModuleBayEditView— count how many installed modules became retroactively incompatible after the edit.is_bay_compatibleonModuleandis_module_compatibleonModuleBayresponses, so automation pipelines can detect and remediate violations.API payload example
GET /api/dcim/module-bays/2/{ "id": 2, "display": "Bay 2", "module_bay_types": [{ "id": 5, "display": "type2" }], "installed_module": { "id": 38, "display": "Module1234" }, "_occupied": true, "is_module_compatible": false }GET /api/dcim/modules/38/{ "id": 38, "display": "Module1234", "module_bay": { "id": 2, "display": "Bay 2" }, "module_type": { "id": 6, "display": "Module1234" }, "status": "active", "is_bay_compatible": false }Test plan
dcim.tests.test_models.ModuleBayTypeCompatibilityTestCase— 16 tests coveringis_bay_compatible,is_module_compatible,get_incompatible_modules(), andget_incompatible_module()dcim.tests.test_api.ModuleBayTypeTestCase— 23 API CRUD/filter tests for the new modeldcim.tests.test_api.ModuleTestCase.test_is_bay_compatible_flag— asserts the flag istrue/false/truefor compatible/incompatible/unconstrained modulesdcim.tests.test_api.ModuleBayTestCase.test_is_module_compatible_flag— same for module baysdcim.tests.test_filtersets.ModuleBayTypeTestCase— 9 filterset testsdcim.tests.test_views.ModuleBayTypeTestCase— 30 view tests (list, detail, create, edit, delete, bulk)ModuleTestCase,ModuleBayTestCase,ModuleTypeTestCase,ModuleBayTemplateTestCasequery-count baselines updated for the new M2M prefetchesGenerated with Claude Code