Add {module_path} placeholder for nested module component templates #350

Closed
opened 2026-04-05 16:27:00 +02:00 by MrUnknownDE · 0 comments
Owner

Originally created by @mrmrcoleman on 2/13/2026

Summary

Introduces a new {module_path} placeholder for module component templates that automatically expands to the full position path for nested modules, joined by /.

Fixes: #20474, #19796

Problem

When module types are nested (e.g. a line card containing SFP bays), users need component names that reflect the full position path — for example SFP 1/2 for an SFP in slot 2 of line card 1.

The existing {module} placeholder requires one token per nesting level ({module}/{module}), which means the same ModuleType cannot be reused at different depths. An SFP module type designed for depth 1 (SFP {module}) fails validation at depth 2 with: "Cannot install module with placeholder values in a module bay tree 2 in tree but 1 placeholders given."

This forces users to create duplicate ModuleTypes for every possible nesting level, which defeats the purpose of modular design.

Solution

A new {module_path} placeholder that expands to the full path regardless of depth:

Depth Positions {module_path} expands to
1 "1" 1
2 "1", "2" 1/2
3 "1", "2", "3" 1/2/3

A single ModuleType with template name SFP {module_path} now works at any nesting depth without modification.

{module} behavior is unchanged

The existing {module} placeholder continues to work exactly as before — level-by-level replacement where the token count must match the nesting depth. No existing workflows are affected.

Validation rules (form layer)

  • {module_path} can only appear once per template attribute
  • {module} and {module_path} cannot be mixed in the same attribute
  • Multiple {module} tokens must still match tree depth exactly

Changes

File Change
dcim/constants.py Added MODULE_PATH_TOKEN and MODULE_TOKEN_SEPARATOR constants
dcim/utils.py New resolve_module_placeholders() — centralized substitution logic for both placeholders
dcim/models/device_component_templates.py Refactored resolve_name() and resolve_label() to use shared _resolve_placeholders() helper
dcim/forms/common.py Extracted _validate_module_tokens() method; updated clean() to validate and resolve both placeholder types
dcim/tests/test_models.py Comprehensive test coverage (see below)
docs/models/dcim/moduletype.md Documentation for {module_path}

Testing

All existing tests pass. Added tests covering:

  • {module} at depth 1 (backward compat, single token → position)
  • {module}/{module} at depth 2 (backward compat, level-by-level)
  • {module_path} at depths 1, 2, and 3
  • Label substitution with {module_path}
  • Non-interface components (ConsolePort) with {module_path}
  • Positions containing slashes (0/1 + 20/1/2)
  • Validation: single {module} rejected at depth > 1
  • Validation: token count mismatch (too many / too few)
  • Validation: mixed {module} + {module_path} rejected
  • Validation: duplicate {module_path} rejected

Not addressed

  • #20467 ({module} in the position field of ModuleBayTemplates) — this is a separate issue requiring changes to position resolution, which is outside the scope of this PR.
*Originally created by @mrmrcoleman on 2/13/2026* ## Summary Introduces a new `{module_path}` placeholder for module component templates that automatically expands to the full position path for nested modules, joined by `/`. **Fixes:** #20474, #19796 ## Problem When module types are nested (e.g. a line card containing SFP bays), users need component names that reflect the full position path — for example `SFP 1/2` for an SFP in slot 2 of line card 1. The existing `{module}` placeholder requires one token per nesting level (`{module}/{module}`), which means the same ModuleType cannot be reused at different depths. An SFP module type designed for depth 1 (`SFP {module}`) fails validation at depth 2 with: *"Cannot install module with placeholder values in a module bay tree 2 in tree but 1 placeholders given."* This forces users to create duplicate ModuleTypes for every possible nesting level, which defeats the purpose of modular design. ## Solution A new `{module_path}` placeholder that expands to the full path regardless of depth: | Depth | Positions | `{module_path}` expands to | |-------|-----------|----------------------------| | 1 | "1" | `1` | | 2 | "1", "2" | `1/2` | | 3 | "1", "2", "3" | `1/2/3` | A single ModuleType with template name `SFP {module_path}` now works at any nesting depth without modification. ### `{module}` behavior is unchanged The existing `{module}` placeholder continues to work exactly as before — level-by-level replacement where the token count must match the nesting depth. No existing workflows are affected. ### Validation rules (form layer) - `{module_path}` can only appear once per template attribute - `{module}` and `{module_path}` cannot be mixed in the same attribute - Multiple `{module}` tokens must still match tree depth exactly ## Changes | File | Change | |------|--------| | `dcim/constants.py` | Added `MODULE_PATH_TOKEN` and `MODULE_TOKEN_SEPARATOR` constants | | `dcim/utils.py` | New `resolve_module_placeholders()` — centralized substitution logic for both placeholders | | `dcim/models/device_component_templates.py` | Refactored `resolve_name()` and `resolve_label()` to use shared `_resolve_placeholders()` helper | | `dcim/forms/common.py` | Extracted `_validate_module_tokens()` method; updated `clean()` to validate and resolve both placeholder types | | `dcim/tests/test_models.py` | Comprehensive test coverage (see below) | | `docs/models/dcim/moduletype.md` | Documentation for `{module_path}` | ## Testing All existing tests pass. Added tests covering: - `{module}` at depth 1 (backward compat, single token → position) - `{module}/{module}` at depth 2 (backward compat, level-by-level) - `{module_path}` at depths 1, 2, and 3 - Label substitution with `{module_path}` - Non-interface components (ConsolePort) with `{module_path}` - Positions containing slashes (`0/1` + `2` → `0/1/2`) - Validation: single `{module}` rejected at depth > 1 - Validation: token count mismatch (too many / too few) - Validation: mixed `{module}` + `{module_path}` rejected - Validation: duplicate `{module_path}` rejected ## Not addressed - #20467 (`{module}` in the `position` field of ModuleBayTemplates) — this is a separate issue requiring changes to position resolution, which is outside the scope of this PR.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github/netbox#350