model_forms.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. from django import forms
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.core.exceptions import ObjectDoesNotExist
  4. from django.utils.translation import gettext_lazy as _
  5. from circuits.choices import (
  6. CircuitCommitRateChoices,
  7. CircuitTerminationPortSpeedChoices,
  8. VirtualCircuitTerminationRoleChoices,
  9. )
  10. from circuits.constants import *
  11. from circuits.models import *
  12. from dcim.models import Interface, Site
  13. from ipam.models import ASN
  14. from netbox.forms import NetBoxModelForm, OrganizationalModelForm, PrimaryModelForm
  15. from tenancy.forms import TenancyForm
  16. from utilities.forms import get_field_value
  17. from utilities.forms.fields import (
  18. ContentTypeChoiceField,
  19. DynamicModelChoiceField,
  20. DynamicModelMultipleChoiceField,
  21. SlugField,
  22. )
  23. from utilities.forms.mixins import DistanceValidationMixin
  24. from utilities.forms.rendering import FieldSet, InlineFields
  25. from utilities.forms.widgets import DatePicker, HTMXSelect, NumberWithOptions
  26. from utilities.templatetags.builtins.filters import bettertitle
  27. __all__ = (
  28. 'CircuitForm',
  29. 'CircuitGroupAssignmentForm',
  30. 'CircuitGroupForm',
  31. 'CircuitTerminationForm',
  32. 'CircuitTypeForm',
  33. 'ProviderAccountForm',
  34. 'ProviderForm',
  35. 'ProviderNetworkForm',
  36. 'VirtualCircuitForm',
  37. 'VirtualCircuitTerminationForm',
  38. 'VirtualCircuitTypeForm',
  39. )
  40. class ProviderForm(PrimaryModelForm):
  41. slug = SlugField()
  42. asns = DynamicModelMultipleChoiceField(
  43. queryset=ASN.objects.all(),
  44. label=_('ASNs'),
  45. required=False
  46. )
  47. fieldsets = (
  48. FieldSet('name', 'slug', 'asns', 'description', 'tags'),
  49. )
  50. class Meta:
  51. model = Provider
  52. fields = [
  53. 'name', 'slug', 'asns', 'description', 'owner', 'comments', 'tags',
  54. ]
  55. class ProviderAccountForm(PrimaryModelForm):
  56. provider = DynamicModelChoiceField(
  57. label=_('Provider'),
  58. queryset=Provider.objects.all(),
  59. selector=True,
  60. quick_add=True
  61. )
  62. class Meta:
  63. model = ProviderAccount
  64. fields = [
  65. 'provider', 'name', 'account', 'description', 'owner', 'comments', 'tags',
  66. ]
  67. class ProviderNetworkForm(PrimaryModelForm):
  68. provider = DynamicModelChoiceField(
  69. label=_('Provider'),
  70. queryset=Provider.objects.all(),
  71. selector=True,
  72. quick_add=True
  73. )
  74. fieldsets = (
  75. FieldSet('provider', 'name', 'service_id', 'description', 'tags'),
  76. )
  77. class Meta:
  78. model = ProviderNetwork
  79. fields = [
  80. 'provider', 'name', 'service_id', 'description', 'owner', 'comments', 'tags',
  81. ]
  82. class CircuitTypeForm(OrganizationalModelForm):
  83. fieldsets = (
  84. FieldSet('name', 'slug', 'color', 'description', 'tags'),
  85. )
  86. class Meta:
  87. model = CircuitType
  88. fields = [
  89. 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
  90. ]
  91. class CircuitForm(DistanceValidationMixin, TenancyForm, PrimaryModelForm):
  92. provider = DynamicModelChoiceField(
  93. label=_('Provider'),
  94. queryset=Provider.objects.all(),
  95. selector=True,
  96. quick_add=True
  97. )
  98. provider_account = DynamicModelChoiceField(
  99. label=_('Provider account'),
  100. queryset=ProviderAccount.objects.all(),
  101. required=False,
  102. query_params={
  103. 'provider_id': '$provider',
  104. }
  105. )
  106. type = DynamicModelChoiceField(
  107. queryset=CircuitType.objects.all(),
  108. quick_add=True
  109. )
  110. fieldsets = (
  111. FieldSet(
  112. 'provider',
  113. 'provider_account',
  114. 'cid',
  115. 'type',
  116. 'status',
  117. InlineFields('distance', 'distance_unit', label=_('Distance')),
  118. 'description',
  119. 'tags',
  120. name=_('Circuit')
  121. ),
  122. FieldSet('install_date', 'termination_date', 'commit_rate', name=_('Service Parameters')),
  123. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  124. )
  125. class Meta:
  126. model = Circuit
  127. fields = [
  128. 'cid', 'type', 'provider', 'provider_account', 'status', 'install_date', 'termination_date', 'commit_rate',
  129. 'distance', 'distance_unit', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
  130. ]
  131. widgets = {
  132. 'install_date': DatePicker(),
  133. 'termination_date': DatePicker(),
  134. 'commit_rate': NumberWithOptions(
  135. options=CircuitCommitRateChoices
  136. ),
  137. }
  138. class CircuitTerminationForm(NetBoxModelForm):
  139. circuit = DynamicModelChoiceField(
  140. label=_('Circuit'),
  141. queryset=Circuit.objects.all(),
  142. selector=True
  143. )
  144. termination_type = ContentTypeChoiceField(
  145. queryset=ContentType.objects.filter(model__in=CIRCUIT_TERMINATION_TERMINATION_TYPES),
  146. widget=HTMXSelect(),
  147. required=False,
  148. label=_('Termination type')
  149. )
  150. termination = DynamicModelChoiceField(
  151. label=_('Termination'),
  152. queryset=Site.objects.none(), # Initial queryset
  153. required=False,
  154. disabled=True,
  155. selector=True
  156. )
  157. fieldsets = (
  158. FieldSet(
  159. 'circuit', 'term_side', 'description', 'tags',
  160. 'termination_type', 'termination',
  161. 'mark_connected', name=_('Circuit Termination')
  162. ),
  163. FieldSet('port_speed', 'upstream_speed', 'xconnect_id', 'pp_info', name=_('Termination Details')),
  164. )
  165. class Meta:
  166. model = CircuitTermination
  167. fields = [
  168. 'circuit', 'term_side', 'termination_type', 'mark_connected', 'port_speed', 'upstream_speed',
  169. 'xconnect_id', 'pp_info', 'description', 'tags',
  170. ]
  171. widgets = {
  172. 'port_speed': NumberWithOptions(
  173. options=CircuitTerminationPortSpeedChoices
  174. ),
  175. 'upstream_speed': NumberWithOptions(
  176. options=CircuitTerminationPortSpeedChoices
  177. ),
  178. }
  179. def __init__(self, *args, **kwargs):
  180. instance = kwargs.get('instance')
  181. initial = kwargs.get('initial', {})
  182. if instance is not None and instance.termination:
  183. initial['termination'] = instance.termination
  184. kwargs['initial'] = initial
  185. super().__init__(*args, **kwargs)
  186. if termination_type_id := get_field_value(self, 'termination_type'):
  187. try:
  188. termination_type = ContentType.objects.get(pk=termination_type_id)
  189. model = termination_type.model_class()
  190. self.fields['termination'].queryset = model.objects.all()
  191. self.fields['termination'].widget.attrs['selector'] = model._meta.label_lower
  192. self.fields['termination'].disabled = False
  193. self.fields['termination'].label = _(bettertitle(model._meta.verbose_name))
  194. except ObjectDoesNotExist:
  195. pass
  196. if self.instance and termination_type_id != self.instance.termination_type_id:
  197. self.initial['termination'] = None
  198. def clean(self):
  199. super().clean()
  200. # Assign the selected termination (if any)
  201. self.instance.termination = self.cleaned_data.get('termination')
  202. class CircuitGroupForm(TenancyForm, OrganizationalModelForm):
  203. fieldsets = (
  204. FieldSet('name', 'slug', 'description', 'tags', name=_('Circuit Group')),
  205. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  206. )
  207. class Meta:
  208. model = CircuitGroup
  209. fields = [
  210. 'name', 'slug', 'description', 'tenant_group', 'tenant', 'owner', 'comments', 'tags',
  211. ]
  212. class CircuitGroupAssignmentForm(NetBoxModelForm):
  213. group = DynamicModelChoiceField(
  214. label=_('Group'),
  215. queryset=CircuitGroup.objects.all(),
  216. )
  217. member_type = ContentTypeChoiceField(
  218. queryset=ContentType.objects.filter(CIRCUIT_GROUP_ASSIGNMENT_MEMBER_MODELS),
  219. widget=HTMXSelect(),
  220. required=False,
  221. label=_('Circuit type')
  222. )
  223. member = DynamicModelChoiceField(
  224. label=_('Circuit'),
  225. queryset=Circuit.objects.none(), # Initial queryset
  226. required=False,
  227. disabled=True,
  228. selector=True
  229. )
  230. fieldsets = (
  231. FieldSet('group', 'member_type', 'member', 'priority', 'tags', name=_('Group Assignment')),
  232. )
  233. class Meta:
  234. model = CircuitGroupAssignment
  235. fields = [
  236. 'group', 'member_type', 'priority', 'tags',
  237. ]
  238. def __init__(self, *args, **kwargs):
  239. instance = kwargs.get('instance')
  240. initial = kwargs.get('initial', {})
  241. if instance is not None and instance.member:
  242. initial['member'] = instance.member
  243. kwargs['initial'] = initial
  244. super().__init__(*args, **kwargs)
  245. if member_type_id := get_field_value(self, 'member_type'):
  246. try:
  247. model = ContentType.objects.get(pk=member_type_id).model_class()
  248. self.fields['member'].queryset = model.objects.all()
  249. self.fields['member'].widget.attrs['selector'] = model._meta.label_lower
  250. self.fields['member'].disabled = False
  251. self.fields['member'].label = _(bettertitle(model._meta.verbose_name))
  252. except ObjectDoesNotExist:
  253. pass
  254. if self.instance.pk and member_type_id != self.instance.member_type_id:
  255. self.initial['member'] = None
  256. def clean(self):
  257. super().clean()
  258. # Assign the selected circuit (if any)
  259. self.instance.member = self.cleaned_data.get('member')
  260. class VirtualCircuitTypeForm(OrganizationalModelForm):
  261. fieldsets = (
  262. FieldSet('name', 'slug', 'color', 'description', 'tags'),
  263. )
  264. class Meta:
  265. model = VirtualCircuitType
  266. fields = [
  267. 'name', 'slug', 'color', 'description', 'owner', 'comments', 'tags',
  268. ]
  269. class VirtualCircuitForm(TenancyForm, PrimaryModelForm):
  270. provider_network = DynamicModelChoiceField(
  271. label=_('Provider network'),
  272. queryset=ProviderNetwork.objects.all(),
  273. selector=True
  274. )
  275. provider_account = DynamicModelChoiceField(
  276. label=_('Provider account'),
  277. queryset=ProviderAccount.objects.all(),
  278. required=False
  279. )
  280. type = DynamicModelChoiceField(
  281. queryset=VirtualCircuitType.objects.all(),
  282. quick_add=True
  283. )
  284. fieldsets = (
  285. FieldSet(
  286. 'provider_network', 'provider_account', 'cid', 'type', 'status', 'description', 'tags',
  287. name=_('Virtual circuit'),
  288. ),
  289. FieldSet('tenant_group', 'tenant', name=_('Tenancy')),
  290. )
  291. class Meta:
  292. model = VirtualCircuit
  293. fields = [
  294. 'cid', 'provider_network', 'provider_account', 'type', 'status', 'description', 'tenant_group', 'tenant',
  295. 'owner', 'comments', 'tags',
  296. ]
  297. class VirtualCircuitTerminationForm(NetBoxModelForm):
  298. virtual_circuit = DynamicModelChoiceField(
  299. label=_('Virtual circuit'),
  300. queryset=VirtualCircuit.objects.all(),
  301. selector=True
  302. )
  303. role = forms.ChoiceField(
  304. choices=VirtualCircuitTerminationRoleChoices,
  305. widget=HTMXSelect(),
  306. label=_('Role')
  307. )
  308. interface = DynamicModelChoiceField(
  309. label=_('Interface'),
  310. queryset=Interface.objects.all(),
  311. selector=True,
  312. query_params={
  313. 'kind': 'virtual',
  314. 'virtual_circuit_termination_id': 'null',
  315. },
  316. context={
  317. 'parent': 'device',
  318. }
  319. )
  320. fieldsets = (
  321. FieldSet('virtual_circuit', 'role', 'interface', 'description', 'tags'),
  322. )
  323. class Meta:
  324. model = VirtualCircuitTermination
  325. fields = [
  326. 'virtual_circuit', 'role', 'interface', 'description', 'tags',
  327. ]