mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-21 02:58:43 -06:00
Refactor: move resolve_module_placeholders from constants.py to utils.py
Constants should only contain constant values, not functions with logic. The helper function now lives in dcim/utils.py alongside other utilities like update_interface_bridges and create_port_mappings.
This commit is contained in:
@@ -82,53 +82,6 @@ MODULE_TOKEN = '{module}'
|
|||||||
MODULE_PATH_TOKEN = '{module_path}'
|
MODULE_PATH_TOKEN = '{module_path}'
|
||||||
MODULE_TOKEN_SEPARATOR = '/'
|
MODULE_TOKEN_SEPARATOR = '/'
|
||||||
|
|
||||||
|
|
||||||
def resolve_module_placeholders(text, positions):
|
|
||||||
"""
|
|
||||||
Substitute {module} and {module_path} placeholders in text with position values.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
text: String potentially containing {module} or {module_path} placeholders
|
|
||||||
positions: List of position strings from the module tree (root to leaf)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Text with placeholders replaced according to these rules:
|
|
||||||
|
|
||||||
{module_path}: Always expands to full path (positions joined by MODULE_TOKEN_SEPARATOR).
|
|
||||||
Can only appear once in the text.
|
|
||||||
|
|
||||||
{module}: If used once, expands to the PARENT module bay position only (last in positions).
|
|
||||||
If used multiple times, each token is replaced level-by-level.
|
|
||||||
|
|
||||||
This design (Option 2 per sigprof's feedback) allows two approaches:
|
|
||||||
1. Use {module_path} for automatic full-path expansion (hardcodes '/' separator)
|
|
||||||
2. Use {module} in position fields to build custom paths with user-controlled separators
|
|
||||||
"""
|
|
||||||
if not text:
|
|
||||||
return text
|
|
||||||
|
|
||||||
result = text
|
|
||||||
|
|
||||||
# Handle {module_path} - always expands to full path
|
|
||||||
if MODULE_PATH_TOKEN in result:
|
|
||||||
full_path = MODULE_TOKEN_SEPARATOR.join(positions) if positions else ''
|
|
||||||
result = result.replace(MODULE_PATH_TOKEN, full_path)
|
|
||||||
|
|
||||||
# Handle {module} - parent-only for single token, level-by-level for multiple
|
|
||||||
if MODULE_TOKEN in result:
|
|
||||||
token_count = result.count(MODULE_TOKEN)
|
|
||||||
if token_count == 1 and positions:
|
|
||||||
# Single {module}: substitute with parent (immediate) bay position only
|
|
||||||
parent_position = positions[-1] if positions else ''
|
|
||||||
result = result.replace(MODULE_TOKEN, parent_position, 1)
|
|
||||||
else:
|
|
||||||
# Multiple {module}: substitute level-by-level (existing behavior)
|
|
||||||
for pos in positions:
|
|
||||||
result = result.replace(MODULE_TOKEN, pos, 1)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
MODULAR_COMPONENT_TEMPLATE_MODELS = Q(
|
MODULAR_COMPONENT_TEMPLATE_MODELS = Q(
|
||||||
app_label='dcim',
|
app_label='dcim',
|
||||||
model__in=(
|
model__in=(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
|
from dcim.utils import resolve_module_placeholders
|
||||||
from utilities.forms import get_field_value
|
from utilities.forms import get_field_value
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from mptt.models import MPTTModel, TreeForeignKey
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models.base import PortMappingBase
|
from dcim.models.base import PortMappingBase
|
||||||
|
from dcim.utils import resolve_module_placeholders
|
||||||
from dcim.models.mixins import InterfaceValidationMixin
|
from dcim.models.mixins import InterfaceValidationMixin
|
||||||
from netbox.models import ChangeLoggedModel
|
from netbox.models import ChangeLoggedModel
|
||||||
from utilities.fields import ColorField, NaturalOrderingField
|
from utilities.fields import ColorField, NaturalOrderingField
|
||||||
|
|||||||
@@ -4,6 +4,54 @@ from django.apps import apps
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import router, transaction
|
from django.db import router, transaction
|
||||||
|
|
||||||
|
from dcim.constants import MODULE_PATH_TOKEN, MODULE_TOKEN, MODULE_TOKEN_SEPARATOR
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_module_placeholders(text, positions):
|
||||||
|
"""
|
||||||
|
Substitute {module} and {module_path} placeholders in text with position values.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: String potentially containing {module} or {module_path} placeholders
|
||||||
|
positions: List of position strings from the module tree (root to leaf)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Text with placeholders replaced according to these rules:
|
||||||
|
|
||||||
|
{module_path}: Always expands to full path (positions joined by MODULE_TOKEN_SEPARATOR).
|
||||||
|
Can only appear once in the text.
|
||||||
|
|
||||||
|
{module}: If used once, expands to the PARENT module bay position only (last in positions).
|
||||||
|
If used multiple times, each token is replaced level-by-level.
|
||||||
|
|
||||||
|
This design (Option 2 per sigprof's feedback) allows two approaches:
|
||||||
|
1. Use {module_path} for automatic full-path expansion (hardcodes '/' separator)
|
||||||
|
2. Use {module} in position fields to build custom paths with user-controlled separators
|
||||||
|
"""
|
||||||
|
if not text:
|
||||||
|
return text
|
||||||
|
|
||||||
|
result = text
|
||||||
|
|
||||||
|
# Handle {module_path} - always expands to full path
|
||||||
|
if MODULE_PATH_TOKEN in result:
|
||||||
|
full_path = MODULE_TOKEN_SEPARATOR.join(positions) if positions else ''
|
||||||
|
result = result.replace(MODULE_PATH_TOKEN, full_path)
|
||||||
|
|
||||||
|
# Handle {module} - parent-only for single token, level-by-level for multiple
|
||||||
|
if MODULE_TOKEN in result:
|
||||||
|
token_count = result.count(MODULE_TOKEN)
|
||||||
|
if token_count == 1 and positions:
|
||||||
|
# Single {module}: substitute with parent (immediate) bay position only
|
||||||
|
parent_position = positions[-1] if positions else ''
|
||||||
|
result = result.replace(MODULE_TOKEN, parent_position, 1)
|
||||||
|
else:
|
||||||
|
# Multiple {module}: substitute level-by-level (existing behavior)
|
||||||
|
for pos in positions:
|
||||||
|
result = result.replace(MODULE_TOKEN, pos, 1)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def compile_path_node(ct_id, object_id):
|
def compile_path_node(ct_id, object_id):
|
||||||
return f'{ct_id}:{object_id}'
|
return f'{ct_id}:{object_id}'
|
||||||
|
|||||||
Reference in New Issue
Block a user