Coverage for circuits/forms/model_forms.py: 88%

108 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from django.contrib.contenttypes.models import ContentType 

2from django.utils.translation import gettext_lazy as _ 

3 

4from circuits.choices import ( 

5 CircuitCommitRateChoices, 

6 CircuitPriorityChoices, 

7 CircuitStatusChoices, 

8 CircuitTerminationPortSpeedChoices, 

9 CircuitTerminationSideChoices, 

10 VirtualCircuitTerminationRoleChoices, 

11) 

12from circuits.constants import * 

13from circuits.models import * 

14from dcim.models import Interface 

15from ipam.models import ASN 

16from netbox.choices import DistanceUnitChoices 

17from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm 

18from tenancy.forms import TenancyForm 

19from utilities.forms import GenericObjectFormMixin, add_blank_choice 

20from utilities.forms.fields import ( 

21 ChoiceField, 

22 DynamicModelChoiceField, 

23 DynamicModelMultipleChoiceField, 

24 GenericObjectChoiceField, 

25 SlugField, 

26 TypedChoiceField, 

27) 

28from utilities.forms.mixins import DistanceValidationMixin 

29from utilities.forms.rendering import FieldSet, InlineFields, M2MAddRemoveFields 

30from utilities.forms.widgets import DatePicker, NumberWithOptions 

31 

32__all__ = ( 

33 'CircuitForm', 

34 'CircuitGroupAssignmentForm', 

35 'CircuitGroupForm', 

36 'CircuitTerminationForm', 

37 'CircuitTypeForm', 

38 'ProviderAccountForm', 

39 'ProviderForm', 

40 'ProviderNetworkForm', 

41 'VirtualCircuitForm', 

42 'VirtualCircuitTerminationForm', 

43 'VirtualCircuitTypeForm', 

44) 

45 

46 

47class ProviderForm(PrimaryModelForm): 

48 slug = SlugField() 

49 asns = DynamicModelMultipleChoiceField( 

50 queryset=ASN.objects.all(), 

51 label=_('ASNs'), 

52 required=False 

53 ) 

54 add_asns = DynamicModelMultipleChoiceField( 

55 queryset=ASN.objects.all(), 

56 label=_('Add ASNs'), 

57 required=False 

58 ) 

59 remove_asns = DynamicModelMultipleChoiceField( 

60 queryset=ASN.objects.all(), 

61 label=_('Remove ASNs'), 

62 required=False 

63 ) 

64 

65 fieldsets = ( 

66 FieldSet('name', 'slug', M2MAddRemoveFields('asns'), 'description', 'tags'), 

67 ) 

68 

69 class Meta: 

70 model = Provider 

71 fields = [ 

72 'name', 'slug', 'description', 'owner', 'comments', 'tags', 

73 ] 

74 

75 def __init__(self, *args, **kwargs): 

76 super().__init__(*args, **kwargs) 

77 if self.instance.pk and (count := self.instance.asns.count()) >= M2MAddRemoveFields.THRESHOLD: 

78 # Add/remove mode for large M2M sets 

79 self.fields.pop('asns') 

80 self.fields['add_asns'].widget.add_query_param('provider_id__n', self.instance.pk) 

81 self.fields['remove_asns'].widget.add_query_param('provider_id', self.instance.pk) 

82 self.fields['remove_asns'].help_text = _("{count} ASNs currently assigned").format(count=count) 

83 else: 

84 # Simple mode for new objects or small M2M sets 

85 self.fields.pop('add_asns') 

86 self.fields.pop('remove_asns') 

87 if self.instance.pk: 

88 self.initial['asns'] = list(self.instance.asns.values_list('pk', flat=True)) 

89 

90 

91class ProviderAccountForm(PrimaryModelForm): 

92 provider = DynamicModelChoiceField( 

93 label=_('Provider'), 

94 queryset=Provider.objects.all(), 

95 selector=True, 

96 quick_add=True 

97 ) 

98 

99 fieldsets = ( 

100 FieldSet('provider', 'account', 'name', 'description', 'tags'), 

101 ) 

102 

103 class Meta: 

104 model = ProviderAccount 

105 fields = [ 

106 'provider', 'account', 'name', 'description', 'owner', 'comments', 'tags', 

107 ] 

108 

109 

110class ProviderNetworkForm(PrimaryModelForm): 

111 provider = DynamicModelChoiceField( 

112 label=_('Provider'), 

113 queryset=Provider.objects.all(), 

114 selector=True, 

115 quick_add=True 

116 ) 

117 

118 fieldsets = ( 

119 FieldSet('provider', 'name', 'service_id', 'description', 'tags'), 

120 ) 

121 

122 class Meta: 

123 model = ProviderNetwork 

124 fields = [ 

125 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags', 

126 ] 

127 

128 

129class CircuitTypeForm(OrganizationalModelForm): 

130 fieldsets = ( 

131 FieldSet('name', 'slug', 'color', 'description', 'tags'), 

132 ) 

133 

134 class Meta: 

135 model = CircuitType 

136 fields = [ 

137 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags', 

138 ] 

139 

140 

141class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm): 

142 status = ChoiceField( 

143 label=_('Status'), 

144 choices=CircuitStatusChoices, 

145 initial=CircuitStatusChoices.STATUS_ACTIVE, 

146 ) 

147 distance_unit = TypedChoiceField( 

148 label=_('Distance unit'), 

149 choices=add_blank_choice(DistanceUnitChoices), 

150 required=False, 

151 ) 

152 provider = DynamicModelChoiceField( 

153 label=_('Provider'), 

154 queryset=Provider.objects.all(), 

155 selector=True, 

156 quick_add=True 

157 ) 

158 provider_account = DynamicModelChoiceField( 

159 label=_('Provider account'), 

160 queryset=ProviderAccount.objects.all(), 

161 required=False, 

162 query_params={ 

163 'provider_id': '$provider', 

164 } 

165 ) 

166 type = DynamicModelChoiceField( 

167 queryset=CircuitType.objects.all(), 

168 quick_add=True 

169 ) 

170 

171 fieldsets = ( 

172 FieldSet( 

173 'provider', 

174 'provider_account', 

175 'cid', 

176 'type', 

177 'status', 

178 InlineFields('distance', 'distance_unit', label=_('Distance')), 

179 'description', 

180 'tags', 

181 name=_('Circuit') 

182 ), 

183 FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')), 

184 FieldSet('tenant_group', 'tenant', name=_('Tenancy')), 

185 ) 

186 

187 class Meta: 

188 model = Circuit 

189 fields = [ 

190 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate', 

191 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags', 

192 ] 

193 widgets = { 

194 'install_date': DatePicker(), 

195 'termination_date': DatePicker(), 

196 'commit_rate': NumberWithOptions( 

197 options=CircuitCommitRateChoices 

198 ), 

199 } 

200 

201 

202class CircuitTerminationForm(GenericObjectFormMixin, NetBoxModelForm): 

203 term_side = ChoiceField( 

204 label=_('Termination side'), 

205 choices=CircuitTerminationSideChoices, 

206 ) 

207 circuit = DynamicModelChoiceField( 

208 label=_('Circuit'), 

209 queryset=Circuit.objects.all(), 

210 selector=True 

211 ) 

212 termination = GenericObjectChoiceField( 

213 label=_('Termination'), 

214 content_type_queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES), 

215 required=True, 

216 selector=True, 

217 hx_target_id='circuit-termination', 

218 ) 

219 

220 fieldsets = ( 

221 FieldSet( 

222 'circuit', 'term_side', 'description', 'tags', 'termination', 'mark_connected', 

223 name=_('Circuit Termination'), html_id='circuit-termination', 

224 ), 

225 FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')), 

226 ) 

227 

228 class Meta: 

229 model = CircuitTermination 

230 fields = [ 

231 'circuit', 'term_side', 'mark_connected', 'port_speed', 'upstream_speed', 

232 'xconnect_id', 'pp_info', 'description', 'tags', 

233 ] 

234 widgets = { 

235 'port_speed': NumberWithOptions( 

236 options=CircuitTerminationPortSpeedChoices 

237 ), 

238 'upstream_speed': NumberWithOptions( 

239 options=CircuitTerminationPortSpeedChoices 

240 ), 

241 } 

242 

243 

244class CircuitGroupForm(TenancyForm, OrganizationalModelForm): 

245 fieldsets = ( 

246 FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')), 

247 FieldSet('tenant_group', 'tenant', name=_('Tenancy')), 

248 ) 

249 

250 class Meta: 

251 model = CircuitGroup 

252 fields = [ 

253 'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags', 

254 ] 

255 

256 

257class CircuitGroupAssignmentForm(GenericObjectFormMixin, NetBoxModelForm): 

258 priority = TypedChoiceField( 

259 label=_('Priority'), 

260 choices=add_blank_choice(CircuitPriorityChoices), 

261 required=False, 

262 ) 

263 group = DynamicModelChoiceField( 

264 label=_('Group'), 

265 queryset=CircuitGroup.objects.all(), 

266 ) 

267 member = GenericObjectChoiceField( 

268 label=_('Member'), 

269 content_type_queryset=ContentType.objects.filter(CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS), 

270 required=False, 

271 selector=True, 

272 hx_target_id='circuit-group-assignment', 

273 ) 

274 

275 fieldsets = ( 

276 FieldSet( 

277 'group', 'member', 'priority', 'tags', 

278 name=_('Group Assignment'), html_id='circuit-group-assignment', 

279 ), 

280 ) 

281 

282 class Meta: 

283 model = CircuitGroupAssignment 

284 fields = [ 

285 'group', 'priority', 'tags', 

286 ] 

287 

288 

289class VirtualCircuitTypeForm(OrganizationalModelForm): 

290 fieldsets = ( 

291 FieldSet('name', 'slug', 'color', 'description', 'tags'), 

292 ) 

293 

294 class Meta: 

295 model = VirtualCircuitType 

296 fields = [ 

297 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags', 

298 ] 

299 

300 

301class VirtualCircuitForm(TenancyForm, PrimaryModelForm): 

302 status = ChoiceField( 

303 label=_('Status'), 

304 choices=CircuitStatusChoices, 

305 initial=CircuitStatusChoices.STATUS_ACTIVE, 

306 ) 

307 provider_network = DynamicModelChoiceField( 

308 label=_('Provider network'), 

309 queryset=ProviderNetwork.objects.all(), 

310 selector=True 

311 ) 

312 provider_account = DynamicModelChoiceField( 

313 label=_('Provider account'), 

314 queryset=ProviderAccount.objects.all(), 

315 required=False 

316 ) 

317 type = DynamicModelChoiceField( 

318 queryset=VirtualCircuitType.objects.all(), 

319 quick_add=True 

320 ) 

321 

322 fieldsets = ( 

323 FieldSet( 

324 'provider_network', 'provider_account', 'cid', 'type', 'status', 'description', 'tags', 

325 name=_('Virtual circuit'), 

326 ), 

327 FieldSet('tenant_group', 'tenant', name=_('Tenancy')), 

328 ) 

329 

330 class Meta: 

331 model = VirtualCircuit 

332 fields = [ 

333 'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant', 

334 'owner', 'comments', 'tags', 

335 ] 

336 

337 

338class VirtualCircuitTerminationForm(NetBoxModelForm): 

339 virtual_circuit = DynamicModelChoiceField( 

340 label=_('Virtual circuit'), 

341 queryset=VirtualCircuit.objects.all(), 

342 selector=True 

343 ) 

344 role = ChoiceField( 

345 choices=VirtualCircuitTerminationRoleChoices, 

346 label=_('Role') 

347 ) 

348 interface = DynamicModelChoiceField( 

349 label=_('Interface'), 

350 queryset=Interface.objects.all(), 

351 selector=True, 

352 query_params={ 

353 'kind': 'virtual', 

354 'virtual_circuit_termination_id': 'null', 

355 }, 

356 context={ 

357 'parent': 'device', 

358 } 

359 ) 

360 

361 fieldsets = ( 

362 FieldSet( 

363 'virtual_circuit', 'role', 'interface', 'description', 'tags', 

364 ), 

365 ) 

366 

367 class Meta: 

368 model = VirtualCircuitTermination 

369 fields = [ 

370 'virtual_circuit', 'role', 'interface', 'description', 'tags', 

371 ]