Coverage for dcim/forms/object_import.py: 47%
146 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 18:35 +0000
1from django import forms
2from django.utils.translation import gettext_lazy as _
4from dcim.choices import InterfacePoEModeChoices, InterfacePoETypeChoices, InterfaceTypeChoices, PortTypeChoices
5from dcim.models import *
6from wireless.choices import WirelessRoleChoices
8__all__ = (
9 'ConsolePortTemplateImportForm',
10 'ConsoleServerPortTemplateImportForm',
11 'CoolingIntakeTemplateImportForm',
12 'CoolingOutflowTemplateImportForm',
13 'DeviceBayTemplateImportForm',
14 'FrontPortTemplateImportForm',
15 'InterfaceTemplateImportForm',
16 'InventoryItemTemplateImportForm',
17 'ModuleBayTemplateImportForm',
18 'PortTemplateMappingImportForm',
19 'PowerOutletTemplateImportForm',
20 'PowerPortTemplateImportForm',
21 'RearPortTemplateImportForm',
22)
25#
26# Component template import forms
27#
29class ConsolePortTemplateImportForm(forms.ModelForm):
31 class Meta:
32 model = ConsolePortTemplate
33 fields = [
34 'device_type', 'module_type', 'name', 'label', 'type', 'description',
35 ]
38class ConsoleServerPortTemplateImportForm(forms.ModelForm):
40 class Meta:
41 model = ConsoleServerPortTemplate
42 fields = [
43 'device_type', 'module_type', 'name', 'label', 'type', 'description',
44 ]
47class PowerPortTemplateImportForm(forms.ModelForm):
49 class Meta:
50 model = PowerPortTemplate
51 fields = [
52 'device_type', 'module_type', 'name', 'label', 'type', 'maximum_draw', 'allocated_draw', 'description',
53 ]
56class PowerOutletTemplateImportForm(forms.ModelForm):
57 power_port = forms.ModelChoiceField(
58 label=_('Power port'),
59 queryset=PowerPortTemplate.objects.all(),
60 to_field_name='name',
61 required=False
62 )
64 class Meta:
65 model = PowerOutletTemplate
66 fields = [
67 'device_type', 'module_type', 'name', 'label', 'type', 'power_port', 'feed_leg', 'description',
68 ]
70 def clean_device_type(self):
71 if device_type := self.cleaned_data['device_type']:
72 power_port = self.fields['power_port']
73 power_port.queryset = power_port.queryset.filter(device_type=device_type)
75 return device_type
77 def clean_module_type(self):
78 if module_type := self.cleaned_data['module_type']:
79 power_port = self.fields['power_port']
80 power_port.queryset = power_port.queryset.filter(module_type=module_type)
82 return module_type
85class CoolingIntakeTemplateImportForm(forms.ModelForm):
87 class Meta:
88 model = CoolingIntakeTemplate
89 fields = [
90 'device_type', 'module_type', 'name', 'label', 'type', 'diameter', 'diameter_unit',
91 'max_flow', 'max_flow_unit', 'description',
92 ]
95class CoolingOutflowTemplateImportForm(forms.ModelForm):
96 cooling_intake = forms.ModelChoiceField(
97 label=_('Cooling intake'),
98 queryset=CoolingIntakeTemplate.objects.all(),
99 to_field_name='name',
100 required=False
101 )
103 class Meta:
104 model = CoolingOutflowTemplate
105 fields = [
106 'device_type', 'module_type', 'name', 'label', 'type', 'diameter', 'diameter_unit',
107 'cooling_intake', 'description',
108 ]
110 def clean_device_type(self):
111 if device_type := self.cleaned_data['device_type']:
112 cooling_intake = self.fields['cooling_intake']
113 cooling_intake.queryset = cooling_intake.queryset.filter(device_type=device_type)
115 return device_type
117 def clean_module_type(self):
118 if module_type := self.cleaned_data['module_type']:
119 cooling_intake = self.fields['cooling_intake']
120 cooling_intake.queryset = cooling_intake.queryset.filter(module_type=module_type)
122 return module_type
125class InterfaceTemplateImportForm(forms.ModelForm):
126 parent = forms.ModelChoiceField(
127 label=_('Parent'),
128 queryset=InterfaceTemplate.objects.all(),
129 to_field_name='name',
130 required=False
131 )
132 type = forms.ChoiceField(
133 label=_('Type'),
134 choices=InterfaceTypeChoices.CHOICES
135 )
136 poe_mode = forms.ChoiceField(
137 choices=InterfacePoEModeChoices,
138 required=False,
139 label=_('PoE mode')
140 )
141 poe_type = forms.ChoiceField(
142 choices=InterfacePoETypeChoices,
143 required=False,
144 label=_('PoE type')
145 )
146 rf_role = forms.ChoiceField(
147 choices=WirelessRoleChoices,
148 required=False,
149 label=_('Wireless role')
150 )
152 class Meta:
153 model = InterfaceTemplate
154 fields = [
155 'device_type', 'module_type', 'name', 'label', 'type', 'channels', 'channel_id', 'parent', 'enabled',
156 'mgmt_only', 'description', 'poe_mode', 'poe_type', 'rf_role',
157 ]
159 def clean_device_type(self):
160 if device_type := self.cleaned_data['device_type']:
161 parent = self.fields['parent']
162 parent.queryset = parent.queryset.filter(device_type=device_type)
164 return device_type
166 def clean_module_type(self):
167 if module_type := self.cleaned_data['module_type']:
168 parent = self.fields['parent']
169 parent.queryset = parent.queryset.filter(module_type=module_type)
171 return module_type
174class FrontPortTemplateImportForm(forms.ModelForm):
175 type = forms.ChoiceField(
176 label=_('Type'),
177 choices=PortTypeChoices.CHOICES
178 )
180 class Meta:
181 model = FrontPortTemplate
182 fields = [
183 'device_type', 'module_type', 'name', 'type', 'color', 'positions', 'label', 'description',
184 ]
187class RearPortTemplateImportForm(forms.ModelForm):
188 type = forms.ChoiceField(
189 label=_('Type'),
190 choices=PortTypeChoices.CHOICES
191 )
193 class Meta:
194 model = RearPortTemplate
195 fields = [
196 'device_type', 'module_type', 'name', 'type', 'color', 'positions', 'label', 'description',
197 ]
200class PortTemplateMappingImportForm(forms.ModelForm):
201 front_port = forms.ModelChoiceField(
202 label=_('Front port'),
203 queryset=FrontPortTemplate.objects.all(),
204 to_field_name='name',
205 )
206 rear_port = forms.ModelChoiceField(
207 label=_('Rear port'),
208 queryset=RearPortTemplate.objects.all(),
209 to_field_name='name',
210 )
212 class Meta:
213 model = PortTemplateMapping
214 fields = [
215 'device_type', 'module_type', 'front_port', 'front_port_position', 'rear_port', 'rear_port_position',
216 ]
218 def clean_device_type(self):
219 if device_type := self.cleaned_data['device_type']:
220 front_port = self.fields['front_port']
221 rear_port = self.fields['rear_port']
222 front_port.queryset = front_port.queryset.filter(device_type=device_type)
223 rear_port.queryset = rear_port.queryset.filter(device_type=device_type)
224 return device_type
226 def clean_module_type(self):
227 if module_type := self.cleaned_data['module_type']:
228 front_port = self.fields['front_port']
229 rear_port = self.fields['rear_port']
230 front_port.queryset = front_port.queryset.filter(module_type=module_type)
231 rear_port.queryset = rear_port.queryset.filter(module_type=module_type)
232 return module_type
235class ModuleBayTemplateImportForm(forms.ModelForm):
236 module_bay_types = forms.ModelMultipleChoiceField(
237 label=_('Module bay types'),
238 queryset=ModuleBayType.objects.all(),
239 to_field_name='name',
240 required=False,
241 )
243 class Meta:
244 model = ModuleBayTemplate
245 fields = [
246 'device_type', 'module_type', 'name', 'label', 'position', 'enabled', 'description',
247 'module_bay_types',
248 ]
250 def clean(self):
251 """
252 Resolve each referenced bay type name against the parent type's own manufacturer, plus
253 bay types having no manufacturer (global), preferring a manufacturer-specific match
254 over a global one. ModuleBayType's unique constraint is on (manufacturer, name), not
255 name alone, so a name can legitimately match both, and the field's name-based lookup
256 resolves every match into cleaned_data rather than picking one.
258 This runs in clean() rather than in clean_module_bay_types() so that it does not depend
259 on the parent having been cleaned first, which would make it sensitive to the order of
260 Meta.fields.
261 """
262 super().clean()
264 module_bay_types = self.cleaned_data.get('module_bay_types')
265 if not module_bay_types:
266 return
268 # If neither parent resolved, leave the field alone: ModularComponentTemplateModel.clean()
269 # rejects a parentless template in _post_clean(), and reporting unresolvable names on top
270 # of that would just be noise.
271 parent = self.cleaned_data.get('device_type') or self.cleaned_data.get('module_type')
272 if parent is None:
273 return
275 by_name = {}
276 for module_bay_type in module_bay_types:
277 if module_bay_type.manufacturer_id not in (None, parent.manufacturer_id):
278 continue
279 existing = by_name.get(module_bay_type.name)
280 if existing is None or module_bay_type.manufacturer_id is not None:
281 by_name[module_bay_type.name] = module_bay_type
283 # A name matching only some other manufacturer's bay type is rejected rather than
284 # resolved to it.
285 for module_bay_type in module_bay_types:
286 if module_bay_type.name not in by_name:
287 raise forms.ValidationError({
288 'module_bay_types': forms.ValidationError(
289 self.fields['module_bay_types'].error_messages['invalid_choice'],
290 code='invalid_choice',
291 params={'value': module_bay_type.name},
292 )
293 })
295 self.cleaned_data['module_bay_types'] = list(by_name.values())
298class DeviceBayTemplateImportForm(forms.ModelForm):
300 class Meta:
301 model = DeviceBayTemplate
302 fields = [
303 'device_type', 'name', 'label', 'enabled', 'description',
304 ]
307class InventoryItemTemplateImportForm(forms.ModelForm):
308 parent = forms.ModelChoiceField(
309 label=_('Parent'),
310 queryset=InventoryItemTemplate.objects.all(),
311 required=False
312 )
313 role = forms.ModelChoiceField(
314 label=_('Role'),
315 queryset=InventoryItemRole.objects.all(),
316 to_field_name='name',
317 required=False
318 )
319 manufacturer = forms.ModelChoiceField(
320 label=_('Manufacturer'),
321 queryset=Manufacturer.objects.all(),
322 to_field_name='name',
323 required=False
324 )
326 class Meta:
327 model = InventoryItemTemplate
328 fields = [
329 'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description',
330 ]
332 def clean_device_type(self):
333 if device_type := self.cleaned_data['device_type']:
334 parent = self.fields['parent']
335 parent.queryset = parent.queryset.filter(device_type=device_type)
337 return device_type
339 def clean_module_type(self):
340 if module_type := self.cleaned_data['module_type']:
341 parent = self.fields['parent']
342 parent.queryset = parent.queryset.filter(module_type=module_type)
344 return module_type