mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
Merge pull request #8504 from netbox-community/8488-plugins-forms
Closes #8488: Support form components for plugins
This commit is contained in:
commit
4347f624d8
78
docs/plugins/development/forms.md
Normal file
78
docs/plugins/development/forms.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Forms
|
||||||
|
|
||||||
|
## Form Classes
|
||||||
|
|
||||||
|
NetBox provides several base form classes for use by plugins. These are documented below.
|
||||||
|
|
||||||
|
* `NetBoxModelForm`
|
||||||
|
* `NetBoxModelCSVForm`
|
||||||
|
* `NetBoxModelBulkEditForm`
|
||||||
|
* `NetBoxModelFilterSetForm`
|
||||||
|
|
||||||
|
### TODO: Include forms reference
|
||||||
|
|
||||||
|
In addition to the [form fields provided by Django](https://docs.djangoproject.com/en/stable/ref/forms/fields/), NetBox provides several field classes for use within forms to handle specific types of data. These can be imported from `utilities.forms.fields` and are documented below.
|
||||||
|
|
||||||
|
## General Purpose Fields
|
||||||
|
|
||||||
|
::: utilities.forms.ColorField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.CommentField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.JSONField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.MACAddressField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.SlugField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
## Dynamic Object Fields
|
||||||
|
|
||||||
|
::: utilities.forms.DynamicModelChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.DynamicModelMultipleChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
## Content Type Fields
|
||||||
|
|
||||||
|
::: utilities.forms.ContentTypeChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.ContentTypeMultipleChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
## CSV Import Fields
|
||||||
|
|
||||||
|
::: utilities.forms.CSVChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.CSVMultipleChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.CSVModelChoiceField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.CSVContentTypeField
|
||||||
|
selection:
|
||||||
|
members: false
|
||||||
|
|
||||||
|
::: utilities.forms.CSVMultipleContentTypeField
|
||||||
|
selection:
|
||||||
|
members: false
|
@ -105,6 +105,7 @@ nav:
|
|||||||
- Models: 'plugins/development/models.md'
|
- Models: 'plugins/development/models.md'
|
||||||
- Views: 'plugins/development/views.md'
|
- Views: 'plugins/development/views.md'
|
||||||
- Tables: 'plugins/development/tables.md'
|
- Tables: 'plugins/development/tables.md'
|
||||||
|
- Forms: 'plugins/development/forms.md'
|
||||||
- Filter Sets: 'plugins/development/filtersets.md'
|
- Filter Sets: 'plugins/development/filtersets.md'
|
||||||
- REST API: 'plugins/development/rest-api.md'
|
- REST API: 'plugins/development/rest-api.md'
|
||||||
- Background Tasks: 'plugins/development/background-tasks.md'
|
- Background Tasks: 'plugins/development/background-tasks.md'
|
||||||
|
@ -2,7 +2,7 @@ from django import forms
|
|||||||
|
|
||||||
from circuits.choices import CircuitStatusChoices
|
from circuits.choices import CircuitStatusChoices
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import add_blank_choice, CommentField, DynamicModelChoiceField, SmallTextarea, StaticSelect
|
from utilities.forms import add_blank_choice, CommentField, DynamicModelChoiceField, SmallTextarea, StaticSelect
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ProviderBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -47,13 +47,12 @@ class ProviderBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
||||||
'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ProviderNetworkBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ProviderNetworkBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ProviderNetwork.objects.all(),
|
queryset=ProviderNetwork.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -75,13 +74,12 @@ class ProviderNetworkBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditFor
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'service_id', 'description', 'comments',
|
||||||
'service_id', 'description', 'comments',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class CircuitTypeBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -91,11 +89,10 @@ class CircuitTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class CircuitBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class CircuitBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Circuit.objects.all(),
|
queryset=Circuit.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -131,7 +128,6 @@ class CircuitBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'tenant', 'commit_rate', 'description', 'comments',
|
||||||
'tenant', 'commit_rate', 'description', 'comments',
|
)
|
||||||
]
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from circuits.choices import CircuitStatusChoices
|
from circuits.choices import CircuitStatusChoices
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderCSVForm(CustomFieldModelCSVForm):
|
class ProviderCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -22,7 +22,7 @@ class ProviderCSVForm(CustomFieldModelCSVForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderNetworkCSVForm(CustomFieldModelCSVForm):
|
class ProviderNetworkCSVForm(NetBoxModelCSVForm):
|
||||||
provider = CSVModelChoiceField(
|
provider = CSVModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -36,7 +36,7 @@ class ProviderNetworkCSVForm(CustomFieldModelCSVForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeCSVForm(CustomFieldModelCSVForm):
|
class CircuitTypeCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -47,7 +47,7 @@ class CircuitTypeCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CircuitCSVForm(CustomFieldModelCSVForm):
|
class CircuitCSVForm(NetBoxModelCSVForm):
|
||||||
provider = CSVModelChoiceField(
|
provider = CSVModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
|
@ -4,7 +4,7 @@ from django.utils.translation import gettext as _
|
|||||||
from circuits.choices import CircuitStatusChoices
|
from circuits.choices import CircuitStatusChoices
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelFilterForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import TenancyFilterForm
|
||||||
from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField
|
from utilities.forms import DynamicModelMultipleChoiceField, StaticSelectMultiple, TagFilterField
|
||||||
|
|
||||||
@ -16,13 +16,13 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderFilterForm(CustomFieldModelFilterForm):
|
class ProviderFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Provider
|
model = Provider
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['asn'],
|
('ASN', ('asn',)),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -49,11 +49,11 @@ class ProviderFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ProviderNetworkFilterForm(CustomFieldModelFilterForm):
|
class ProviderNetworkFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ProviderNetwork
|
model = ProviderNetwork
|
||||||
field_groups = (
|
fieldsets = (
|
||||||
('q', 'tag'),
|
(None, ('q', 'tag')),
|
||||||
('provider_id',),
|
('Attributes', ('provider_id', 'service_id')),
|
||||||
)
|
)
|
||||||
provider_id = DynamicModelMultipleChoiceField(
|
provider_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
@ -67,20 +67,20 @@ class ProviderNetworkFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeFilterForm(CustomFieldModelFilterForm):
|
class CircuitTypeFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = CircuitType
|
model = CircuitType
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class CircuitFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class CircuitFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Circuit
|
model = Circuit
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['provider_id', 'provider_network_id'],
|
('Provider', ('provider_id', 'provider_network_id')),
|
||||||
['type_id', 'status', 'commit_rate'],
|
('Attributes', ('type_id', 'status', 'commit_rate')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
type_id = DynamicModelMultipleChoiceField(
|
type_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=CircuitType.objects.all(),
|
queryset=CircuitType.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -2,8 +2,8 @@ from django import forms
|
|||||||
|
|
||||||
from circuits.models import *
|
from circuits.models import *
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
BootstrapMixin, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
BootstrapMixin, CommentField, DatePicker, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
||||||
@ -19,7 +19,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProviderForm(CustomFieldModelForm):
|
class ProviderForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
comments = CommentField()
|
comments = CommentField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
@ -27,15 +27,16 @@ class ProviderForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Provider', ('name', 'slug', 'asn', 'tags')),
|
||||||
|
('Support Info', ('account', 'portal_url', 'noc_contact', 'admin_contact')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Provider
|
model = Provider
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
|
'name', 'slug', 'asn', 'account', 'portal_url', 'noc_contact', 'admin_contact', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Provider', ('name', 'slug', 'asn', 'tags')),
|
|
||||||
('Support Info', ('account', 'portal_url', 'noc_contact', 'admin_contact')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'noc_contact': SmallTextarea(
|
'noc_contact': SmallTextarea(
|
||||||
attrs={'rows': 5}
|
attrs={'rows': 5}
|
||||||
@ -53,7 +54,7 @@ class ProviderForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ProviderNetworkForm(CustomFieldModelForm):
|
class ProviderNetworkForm(NetBoxModelForm):
|
||||||
provider = DynamicModelChoiceField(
|
provider = DynamicModelChoiceField(
|
||||||
queryset=Provider.objects.all()
|
queryset=Provider.objects.all()
|
||||||
)
|
)
|
||||||
@ -63,17 +64,18 @@ class ProviderNetworkForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Provider Network', ('provider', 'name', 'service_id', 'description', 'tags')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ProviderNetwork
|
model = ProviderNetwork
|
||||||
fields = [
|
fields = [
|
||||||
'provider', 'name', 'service_id', 'description', 'comments', 'tags',
|
'provider', 'name', 'service_id', 'description', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Provider Network', ('provider', 'name', 'service_id', 'description', 'tags')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CircuitTypeForm(CustomFieldModelForm):
|
class CircuitTypeForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -87,7 +89,7 @@ class CircuitTypeForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CircuitForm(TenancyForm, CustomFieldModelForm):
|
class CircuitForm(TenancyForm, NetBoxModelForm):
|
||||||
provider = DynamicModelChoiceField(
|
provider = DynamicModelChoiceField(
|
||||||
queryset=Provider.objects.all()
|
queryset=Provider.objects.all()
|
||||||
)
|
)
|
||||||
@ -100,16 +102,17 @@ class CircuitForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Circuit', ('provider', 'cid', 'type', 'status', 'install_date', 'commit_rate', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Circuit
|
model = Circuit
|
||||||
fields = [
|
fields = [
|
||||||
'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
|
'cid', 'type', 'provider', 'status', 'install_date', 'commit_rate', 'description', 'tenant_group', 'tenant',
|
||||||
'comments', 'tags',
|
'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Circuit', ('provider', 'cid', 'type', 'status', 'install_date', 'commit_rate', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'cid': "Unique circuit ID",
|
'cid': "Unique circuit ID",
|
||||||
'commit_rate': "Committed rate",
|
'commit_rate': "Committed rate",
|
||||||
|
@ -6,8 +6,8 @@ from timezone_field import TimeZoneFormField
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|
||||||
from ipam.models import ASN, VLAN, VRF
|
from ipam.models import ASN, VLAN, VRF
|
||||||
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
|
add_blank_choice, BulkEditForm, BulkEditNullBooleanSelect, ColorField, CommentField, DynamicModelChoiceField,
|
||||||
@ -57,7 +57,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegionBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RegionBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -71,11 +71,10 @@ class RegionBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'description')
|
||||||
nullable_fields = ['parent', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -89,11 +88,10 @@ class SiteGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'description')
|
||||||
nullable_fields = ['parent', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class SiteBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class SiteBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -131,13 +129,12 @@ class SiteBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
widget=StaticSelect()
|
widget=StaticSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'region', 'group', 'tenant', 'asns', 'description', 'time_zone',
|
||||||
'region', 'group', 'tenant', 'asns', 'description', 'time_zone',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class LocationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class LocationBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Location.objects.all(),
|
queryset=Location.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -162,11 +159,10 @@ class LocationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'tenant', 'description')
|
||||||
nullable_fields = ['parent', 'tenant', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class RackRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=RackRole.objects.all(),
|
queryset=RackRole.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -179,11 +175,10 @@ class RackRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('color', 'description')
|
||||||
nullable_fields = ['color', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class RackBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RackBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Rack.objects.all(),
|
queryset=Rack.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -277,13 +272,12 @@ class RackBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
|
||||||
'location', 'tenant', 'role', 'serial', 'asset_tag', 'outer_width', 'outer_depth', 'outer_unit', 'comments',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RackReservationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RackReservationBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=RackReservation.objects.all(),
|
queryset=RackReservation.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -304,11 +298,8 @@ class RackReservationBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditFor
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
nullable_fields = []
|
|
||||||
|
|
||||||
|
class ManufacturerBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
class ManufacturerBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -318,11 +309,10 @@ class ManufacturerBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class DeviceTypeBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=DeviceType.objects.all(),
|
queryset=DeviceType.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -349,11 +339,10 @@ class DeviceTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
widget=StaticSelect()
|
widget=StaticSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('part_number', 'airflow')
|
||||||
nullable_fields = ['part_number', 'airflow']
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ModuleTypeBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ModuleType.objects.all(),
|
queryset=ModuleType.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -366,11 +355,10 @@ class ModuleTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('part_number',)
|
||||||
nullable_fields = ['part_number']
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class DeviceRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -388,11 +376,10 @@ class DeviceRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('color', 'description')
|
||||||
nullable_fields = ['color', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class PlatformBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class PlatformBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Platform.objects.all(),
|
queryset=Platform.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -411,11 +398,10 @@ class PlatformBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('manufacturer', 'napalm_driver', 'description')
|
||||||
nullable_fields = ['manufacturer', 'napalm_driver', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class DeviceBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -470,13 +456,12 @@ class DeviceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Serial Number'
|
label='Serial Number'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'tenant', 'platform', 'serial', 'airflow',
|
||||||
'tenant', 'platform', 'serial', 'airflow',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ModuleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Module.objects.all(),
|
queryset=Module.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -498,11 +483,10 @@ class ModuleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Serial Number'
|
label='Serial Number'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('serial',)
|
||||||
nullable_fields = ['serial']
|
|
||||||
|
|
||||||
|
|
||||||
class CableBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class CableBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Cable.objects.all(),
|
queryset=Cable.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -541,10 +525,9 @@ class CableBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
widget=StaticSelect()
|
widget=StaticSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'type', 'status', 'tenant', 'label', 'color', 'length',
|
||||||
'type', 'status', 'tenant', 'label', 'color', 'length',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
super().clean()
|
super().clean()
|
||||||
@ -558,7 +541,7 @@ class CableBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class VirtualChassisBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VirtualChassisBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VirtualChassis.objects.all(),
|
queryset=VirtualChassis.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -568,11 +551,10 @@ class VirtualChassisBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('domain',)
|
||||||
nullable_fields = ['domain']
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class PowerPanelBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=PowerPanel.objects.all(),
|
queryset=PowerPanel.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -607,11 +589,10 @@ class PowerPanelBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('location',)
|
||||||
nullable_fields = ['location']
|
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class PowerFeedBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=PowerFeed.objects.all(),
|
queryset=PowerFeed.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -666,10 +647,7 @@ class PowerFeedBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('location', 'comments')
|
||||||
nullable_fields = [
|
|
||||||
'location', 'comments',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -691,8 +669,7 @@ class ConsolePortTemplateBulkEditForm(BulkEditForm):
|
|||||||
widget=StaticSelect()
|
widget=StaticSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'type', 'description')
|
||||||
nullable_fields = ('label', 'type', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortTemplateBulkEditForm(BulkEditForm):
|
class ConsoleServerPortTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -713,8 +690,7 @@ class ConsoleServerPortTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'type', 'description')
|
||||||
nullable_fields = ('label', 'type', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPortTemplateBulkEditForm(BulkEditForm):
|
class PowerPortTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -745,8 +721,7 @@ class PowerPortTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description')
|
||||||
nullable_fields = ('label', 'type', 'maximum_draw', 'allocated_draw', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletTemplateBulkEditForm(BulkEditForm):
|
class PowerOutletTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -782,8 +757,7 @@ class PowerOutletTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description')
|
||||||
nullable_fields = ('label', 'type', 'power_port', 'feed_leg', 'description')
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -820,8 +794,7 @@ class InterfaceTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ('label', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class FrontPortTemplateBulkEditForm(BulkEditForm):
|
class FrontPortTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -845,8 +818,7 @@ class FrontPortTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ('description',)
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortTemplateBulkEditForm(BulkEditForm):
|
class RearPortTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -870,8 +842,7 @@ class RearPortTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ('description',)
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayTemplateBulkEditForm(BulkEditForm):
|
class ModuleBayTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -887,8 +858,7 @@ class ModuleBayTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'position', 'description')
|
||||||
nullable_fields = ('label', 'position', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayTemplateBulkEditForm(BulkEditForm):
|
class DeviceBayTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -904,8 +874,7 @@ class DeviceBayTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ('label', 'description')
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemTemplateBulkEditForm(BulkEditForm):
|
class InventoryItemTemplateBulkEditForm(BulkEditForm):
|
||||||
@ -929,8 +898,7 @@ class InventoryItemTemplateBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')
|
||||||
nullable_fields = ['label', 'role', 'manufacturer', 'part_id', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -939,8 +907,7 @@ class InventoryItemTemplateBulkEditForm(BulkEditForm):
|
|||||||
|
|
||||||
class ConsolePortBulkEditForm(
|
class ConsolePortBulkEditForm(
|
||||||
form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']),
|
form_from_model(ConsolePort, ['label', 'type', 'speed', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ConsolePort.objects.all(),
|
queryset=ConsolePort.objects.all(),
|
||||||
@ -951,14 +918,12 @@ class ConsolePortBulkEditForm(
|
|||||||
widget=BulkEditNullBooleanSelect
|
widget=BulkEditNullBooleanSelect
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortBulkEditForm(
|
class ConsoleServerPortBulkEditForm(
|
||||||
form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description']),
|
form_from_model(ConsoleServerPort, ['label', 'type', 'speed', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ConsoleServerPort.objects.all(),
|
queryset=ConsoleServerPort.objects.all(),
|
||||||
@ -969,14 +934,12 @@ class ConsoleServerPortBulkEditForm(
|
|||||||
widget=BulkEditNullBooleanSelect
|
widget=BulkEditNullBooleanSelect
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class PowerPortBulkEditForm(
|
class PowerPortBulkEditForm(
|
||||||
form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description']),
|
form_from_model(PowerPort, ['label', 'type', 'maximum_draw', 'allocated_draw', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=PowerPort.objects.all(),
|
queryset=PowerPort.objects.all(),
|
||||||
@ -987,14 +950,12 @@ class PowerPortBulkEditForm(
|
|||||||
widget=BulkEditNullBooleanSelect
|
widget=BulkEditNullBooleanSelect
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletBulkEditForm(
|
class PowerOutletBulkEditForm(
|
||||||
form_from_model(PowerOutlet, ['label', 'type', 'feed_leg', 'power_port', 'mark_connected', 'description']),
|
form_from_model(PowerOutlet, ['label', 'type', 'feed_leg', 'power_port', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=PowerOutlet.objects.all(),
|
queryset=PowerOutlet.objects.all(),
|
||||||
@ -1011,8 +972,7 @@ class PowerOutletBulkEditForm(
|
|||||||
widget=BulkEditNullBooleanSelect
|
widget=BulkEditNullBooleanSelect
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'type', 'feed_leg', 'power_port', 'description')
|
||||||
nullable_fields = ['label', 'type', 'feed_leg', 'power_port', 'description']
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -1031,8 +991,7 @@ class InterfaceBulkEditForm(
|
|||||||
'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
|
'label', 'type', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'mgmt_only', 'mark_connected',
|
||||||
'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power',
|
||||||
]),
|
]),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
@ -1092,11 +1051,10 @@ class InterfaceBulkEditForm(
|
|||||||
label='VRF'
|
label='VRF'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'description', 'mode',
|
||||||
'label', 'parent', 'bridge', 'lag', 'speed', 'duplex', 'mac_address', 'wwn', 'mtu', 'description', 'mode', 'rf_channel',
|
'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf',
|
||||||
'rf_channel_frequency', 'rf_channel_width', 'tx_power', 'untagged_vlan', 'tagged_vlans', 'vrf',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -1154,64 +1112,55 @@ class InterfaceBulkEditForm(
|
|||||||
|
|
||||||
class FrontPortBulkEditForm(
|
class FrontPortBulkEditForm(
|
||||||
form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']),
|
form_from_model(FrontPort, ['label', 'type', 'color', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=FrontPort.objects.all(),
|
queryset=FrontPort.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class RearPortBulkEditForm(
|
class RearPortBulkEditForm(
|
||||||
form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']),
|
form_from_model(RearPort, ['label', 'type', 'color', 'mark_connected', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=RearPort.objects.all(),
|
queryset=RearPort.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayBulkEditForm(
|
class ModuleBayBulkEditForm(
|
||||||
form_from_model(DeviceBay, ['label', 'description']),
|
form_from_model(DeviceBay, ['label', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ModuleBay.objects.all(),
|
queryset=ModuleBay.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'position', 'description')
|
||||||
nullable_fields = ['label', 'position', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayBulkEditForm(
|
class DeviceBayBulkEditForm(
|
||||||
form_from_model(DeviceBay, ['label', 'description']),
|
form_from_model(DeviceBay, ['label', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=DeviceBay.objects.all(),
|
queryset=DeviceBay.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'description')
|
||||||
nullable_fields = ['label', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemBulkEditForm(
|
class InventoryItemBulkEditForm(
|
||||||
form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']),
|
form_from_model(InventoryItem, ['label', 'role', 'manufacturer', 'part_id', 'description']),
|
||||||
AddRemoveTagsForm,
|
NetBoxModelBulkEditForm
|
||||||
CustomFieldModelBulkEditForm
|
|
||||||
):
|
):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
@ -1226,15 +1175,14 @@ class InventoryItemBulkEditForm(
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('label', 'role', 'manufacturer', 'part_id', 'description')
|
||||||
nullable_fields = ['label', 'role', 'manufacturer', 'part_id', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Device component roles
|
# Device component roles
|
||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class InventoryItemRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=InventoryItemRole.objects.all(),
|
queryset=InventoryItemRole.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -1247,5 +1195,4 @@ class InventoryItemRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditF
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('color', 'description')
|
||||||
nullable_fields = ['color', 'description']
|
|
||||||
|
@ -7,8 +7,8 @@ from django.utils.safestring import mark_safe
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
|
||||||
from ipam.models import VRF
|
from ipam.models import VRF
|
||||||
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVTypedChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, CSVTypedChoiceField, SlugField
|
||||||
from virtualization.models import Cluster
|
from virtualization.models import Cluster
|
||||||
@ -46,7 +46,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegionCSVForm(CustomFieldModelCSVForm):
|
class RegionCSVForm(NetBoxModelCSVForm):
|
||||||
parent = CSVModelChoiceField(
|
parent = CSVModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -59,7 +59,7 @@ class RegionCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'parent', 'description')
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupCSVForm(CustomFieldModelCSVForm):
|
class SiteGroupCSVForm(NetBoxModelCSVForm):
|
||||||
parent = CSVModelChoiceField(
|
parent = CSVModelChoiceField(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -72,7 +72,7 @@ class SiteGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'parent', 'description')
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class SiteCSVForm(CustomFieldModelCSVForm):
|
class SiteCSVForm(NetBoxModelCSVForm):
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=SiteStatusChoices,
|
choices=SiteStatusChoices,
|
||||||
help_text='Operational status'
|
help_text='Operational status'
|
||||||
@ -109,7 +109,7 @@ class SiteCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LocationCSVForm(CustomFieldModelCSVForm):
|
class LocationCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -136,7 +136,7 @@ class LocationCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('site', 'parent', 'name', 'slug', 'tenant', 'description')
|
fields = ('site', 'parent', 'name', 'slug', 'tenant', 'description')
|
||||||
|
|
||||||
|
|
||||||
class RackRoleCSVForm(CustomFieldModelCSVForm):
|
class RackRoleCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -147,7 +147,7 @@ class RackRoleCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackCSVForm(CustomFieldModelCSVForm):
|
class RackCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -205,7 +205,7 @@ class RackCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
|
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
|
||||||
|
|
||||||
|
|
||||||
class RackReservationCSVForm(CustomFieldModelCSVForm):
|
class RackReservationCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -255,14 +255,14 @@ class RackReservationCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
|
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerCSVForm(CustomFieldModelCSVForm):
|
class ManufacturerCSVForm(NetBoxModelCSVForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
fields = ('name', 'slug', 'description')
|
fields = ('name', 'slug', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleCSVForm(CustomFieldModelCSVForm):
|
class DeviceRoleCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -273,7 +273,7 @@ class DeviceRoleCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PlatformCSVForm(CustomFieldModelCSVForm):
|
class PlatformCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
manufacturer = CSVModelChoiceField(
|
manufacturer = CSVModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
@ -287,7 +287,7 @@ class PlatformCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description')
|
fields = ('name', 'slug', 'manufacturer', 'napalm_driver', 'napalm_args', 'description')
|
||||||
|
|
||||||
|
|
||||||
class BaseDeviceCSVForm(CustomFieldModelCSVForm):
|
class BaseDeviceCSVForm(NetBoxModelCSVForm):
|
||||||
device_role = CSVModelChoiceField(
|
device_role = CSVModelChoiceField(
|
||||||
queryset=DeviceRole.objects.all(),
|
queryset=DeviceRole.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -403,7 +403,7 @@ class DeviceCSVForm(BaseDeviceCSVForm):
|
|||||||
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
|
self.fields['rack'].queryset = self.fields['rack'].queryset.filter(**params)
|
||||||
|
|
||||||
|
|
||||||
class ModuleCSVForm(CustomFieldModelCSVForm):
|
class ModuleCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -478,7 +478,7 @@ class ChildDeviceCSVForm(BaseDeviceCSVForm):
|
|||||||
# Device components
|
# Device components
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsolePortCSVForm(CustomFieldModelCSVForm):
|
class ConsolePortCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -501,7 +501,7 @@ class ConsolePortCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description')
|
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
|
class ConsoleServerPortCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -524,7 +524,7 @@ class ConsoleServerPortCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description')
|
fields = ('device', 'name', 'label', 'type', 'speed', 'mark_connected', 'description')
|
||||||
|
|
||||||
|
|
||||||
class PowerPortCSVForm(CustomFieldModelCSVForm):
|
class PowerPortCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -542,7 +542,7 @@ class PowerPortCSVForm(CustomFieldModelCSVForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletCSVForm(CustomFieldModelCSVForm):
|
class PowerOutletCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -591,7 +591,7 @@ class PowerOutletCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['power_port'].queryset = PowerPort.objects.none()
|
self.fields['power_port'].queryset = PowerPort.objects.none()
|
||||||
|
|
||||||
|
|
||||||
class InterfaceCSVForm(CustomFieldModelCSVForm):
|
class InterfaceCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -655,7 +655,7 @@ class InterfaceCSVForm(CustomFieldModelCSVForm):
|
|||||||
return self.cleaned_data['enabled']
|
return self.cleaned_data['enabled']
|
||||||
|
|
||||||
|
|
||||||
class FrontPortCSVForm(CustomFieldModelCSVForm):
|
class FrontPortCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -703,7 +703,7 @@ class FrontPortCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['rear_port'].queryset = RearPort.objects.none()
|
self.fields['rear_port'].queryset = RearPort.objects.none()
|
||||||
|
|
||||||
|
|
||||||
class RearPortCSVForm(CustomFieldModelCSVForm):
|
class RearPortCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -721,7 +721,7 @@ class RearPortCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayCSVForm(CustomFieldModelCSVForm):
|
class ModuleBayCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -732,7 +732,7 @@ class ModuleBayCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('device', 'name', 'label', 'position', 'description')
|
fields = ('device', 'name', 'label', 'position', 'description')
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayCSVForm(CustomFieldModelCSVForm):
|
class DeviceBayCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -778,7 +778,7 @@ class DeviceBayCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['installed_device'].queryset = Interface.objects.none()
|
self.fields['installed_device'].queryset = Interface.objects.none()
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemCSVForm(CustomFieldModelCSVForm):
|
class InventoryItemCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
@ -827,7 +827,7 @@ class InventoryItemCSVForm(CustomFieldModelCSVForm):
|
|||||||
# Device component roles
|
# Device component roles
|
||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemRoleCSVForm(CustomFieldModelCSVForm):
|
class InventoryItemRoleCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -842,7 +842,7 @@ class InventoryItemRoleCSVForm(CustomFieldModelCSVForm):
|
|||||||
# Cables
|
# Cables
|
||||||
#
|
#
|
||||||
|
|
||||||
class CableCSVForm(CustomFieldModelCSVForm):
|
class CableCSVForm(NetBoxModelCSVForm):
|
||||||
# Termination A
|
# Termination A
|
||||||
side_a_device = CSVModelChoiceField(
|
side_a_device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
@ -947,7 +947,7 @@ class CableCSVForm(CustomFieldModelCSVForm):
|
|||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
#
|
#
|
||||||
|
|
||||||
class VirtualChassisCSVForm(CustomFieldModelCSVForm):
|
class VirtualChassisCSVForm(NetBoxModelCSVForm):
|
||||||
master = CSVModelChoiceField(
|
master = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -964,7 +964,7 @@ class VirtualChassisCSVForm(CustomFieldModelCSVForm):
|
|||||||
# Power
|
# Power
|
||||||
#
|
#
|
||||||
|
|
||||||
class PowerPanelCSVForm(CustomFieldModelCSVForm):
|
class PowerPanelCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -990,7 +990,7 @@ class PowerPanelCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
|
self.fields['location'].queryset = self.fields['location'].queryset.filter(**params)
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedCSVForm(CustomFieldModelCSVForm):
|
class PowerFeedCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from circuits.models import Circuit, CircuitTermination, Provider
|
from circuits.models import Circuit, CircuitTermination, Provider
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
|
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToDeviceForm(TenancyForm, CustomFieldModelForm):
|
class ConnectCableToDeviceForm(TenancyForm, NetBoxModelForm):
|
||||||
"""
|
"""
|
||||||
Base form for connecting a Cable to a Device component
|
Base form for connecting a Cable to a Device component
|
||||||
"""
|
"""
|
||||||
@ -171,7 +171,7 @@ class ConnectCableToRearPortForm(ConnectCableToDeviceForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToCircuitTerminationForm(TenancyForm, CustomFieldModelForm):
|
class ConnectCableToCircuitTerminationForm(TenancyForm, NetBoxModelForm):
|
||||||
termination_b_provider = DynamicModelChoiceField(
|
termination_b_provider = DynamicModelChoiceField(
|
||||||
queryset=Provider.objects.all(),
|
queryset=Provider.objects.all(),
|
||||||
label='Provider',
|
label='Provider',
|
||||||
@ -229,7 +229,7 @@ class ConnectCableToCircuitTerminationForm(TenancyForm, CustomFieldModelForm):
|
|||||||
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
|
return getattr(self.cleaned_data['termination_b_id'], 'pk', None)
|
||||||
|
|
||||||
|
|
||||||
class ConnectCableToPowerFeedForm(TenancyForm, CustomFieldModelForm):
|
class ConnectCableToPowerFeedForm(TenancyForm, NetBoxModelForm):
|
||||||
termination_b_region = DynamicModelChoiceField(
|
termination_b_region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
label='Region',
|
label='Region',
|
||||||
|
@ -5,8 +5,9 @@ from django.utils.translation import gettext as _
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm
|
from extras.forms import LocalConfigContextFilterForm
|
||||||
from ipam.models import ASN, VRF
|
from ipam.models import ASN, VRF
|
||||||
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import TenancyFilterForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
APISelectMultiple, add_blank_choice, ColorField, DynamicModelMultipleChoiceField, FilterForm, StaticSelect,
|
||||||
@ -52,7 +53,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceComponentFilterForm(CustomFieldModelFilterForm):
|
class DeviceComponentFilterForm(NetBoxModelFilterSetForm):
|
||||||
name = forms.CharField(
|
name = forms.CharField(
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
@ -103,7 +104,7 @@ class DeviceComponentFilterForm(CustomFieldModelFilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RegionFilterForm(CustomFieldModelFilterForm):
|
class RegionFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Region
|
model = Region
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -113,7 +114,7 @@ class RegionFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupFilterForm(CustomFieldModelFilterForm):
|
class SiteGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = SiteGroup
|
model = SiteGroup
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
@ -123,14 +124,13 @@ class SiteGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class SiteFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Site
|
model = Site
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['status', 'region_id', 'group_id'],
|
('Attributes', ('status', 'region_id', 'group_id', 'asn_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
['asn_id']
|
)
|
||||||
]
|
|
||||||
status = forms.MultipleChoiceField(
|
status = forms.MultipleChoiceField(
|
||||||
choices=SiteStatusChoices,
|
choices=SiteStatusChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -154,13 +154,13 @@ class SiteFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class LocationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Location
|
model = Location
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'parent_id'],
|
('Parent', ('region_id', 'site_group_id', 'site_id', 'parent_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -192,20 +192,20 @@ class LocationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RackRoleFilterForm(CustomFieldModelFilterForm):
|
class RackRoleFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = RackRole
|
model = RackRole
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RackFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class RackFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Rack
|
model = Rack
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_id', 'location_id'],
|
('Location', ('region_id', 'site_id', 'location_id')),
|
||||||
['status', 'role_id'],
|
('Function', ('status', 'role_id')),
|
||||||
['type', 'width', 'serial', 'asset_tag'],
|
('Hardware', ('type', 'width', 'serial', 'asset_tag')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -270,14 +270,14 @@ class RackElevationFilterForm(RackFilterForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class RackReservationFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['user_id'],
|
('User', ('user_id',)),
|
||||||
['region_id', 'site_id', 'location_id'],
|
('Rack', ('region_id', 'site_id', 'location_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -308,18 +308,21 @@ class RackReservationFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerFilterForm(CustomFieldModelFilterForm):
|
class ManufacturerFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Manufacturer
|
model = Manufacturer
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeFilterForm(CustomFieldModelFilterForm):
|
class DeviceTypeFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['manufacturer_id', 'part_number', 'subdevice_role', 'airflow'],
|
('Hardware', ('manufacturer_id', 'part_number', 'subdevice_role', 'airflow')),
|
||||||
['console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports'],
|
('Components', (
|
||||||
]
|
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
|
||||||
|
'pass_through_ports',
|
||||||
|
)),
|
||||||
|
)
|
||||||
manufacturer_id = DynamicModelMultipleChoiceField(
|
manufacturer_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -383,13 +386,16 @@ class DeviceTypeFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ModuleTypeFilterForm(CustomFieldModelFilterForm):
|
class ModuleTypeFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ModuleType
|
model = ModuleType
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['manufacturer_id', 'part_number'],
|
('Hardware', ('manufacturer_id', 'part_number')),
|
||||||
['console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports'],
|
('Components', (
|
||||||
]
|
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces',
|
||||||
|
'pass_through_ports',
|
||||||
|
)),
|
||||||
|
)
|
||||||
manufacturer_id = DynamicModelMultipleChoiceField(
|
manufacturer_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -444,12 +450,12 @@ class ModuleTypeFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleFilterForm(CustomFieldModelFilterForm):
|
class DeviceRoleFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = DeviceRole
|
model = DeviceRole
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class PlatformFilterForm(CustomFieldModelFilterForm):
|
class PlatformFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Platform
|
model = Platform
|
||||||
manufacturer_id = DynamicModelMultipleChoiceField(
|
manufacturer_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
@ -459,19 +465,19 @@ class PlatformFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldModelFilterForm):
|
class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Device
|
model = Device
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
|
||||||
['status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address'],
|
('Operation', ('status', 'role_id', 'airflow', 'serial', 'asset_tag', 'mac_address')),
|
||||||
['manufacturer_id', 'device_type_id', 'platform_id'],
|
('Hardware', ('manufacturer_id', 'device_type_id', 'platform_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
[
|
('Components', (
|
||||||
'has_primary_ip', 'virtual_chassis_member', 'console_ports', 'console_server_ports', 'power_ports',
|
'console_ports', 'console_server_ports', 'power_ports', 'power_outlets', 'interfaces', 'pass_through_ports',
|
||||||
'power_outlets', 'interfaces', 'pass_through_ports', 'local_context_data',
|
)),
|
||||||
],
|
('Miscellaneous', ('has_primary_ip', 'virtual_chassis_member', 'local_context_data'))
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -613,13 +619,12 @@ class DeviceFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldModelFilterForm):
|
class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Module
|
model = Module
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['manufacturer_id', 'module_type_id'],
|
('Hardware', ('manufacturer_id', 'module_type_id', 'serial', 'asset_tag')),
|
||||||
['serial', 'asset_tag'],
|
)
|
||||||
]
|
|
||||||
manufacturer_id = DynamicModelMultipleChoiceField(
|
manufacturer_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -644,13 +649,13 @@ class ModuleFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFi
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VirtualChassisFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class VirtualChassisFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = VirtualChassis
|
model = VirtualChassis
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -673,14 +678,14 @@ class VirtualChassisFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class CableFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Cable
|
model = Cable
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['site_id', 'rack_id', 'device_id'],
|
('Location', ('site_id', 'rack_id', 'device_id')),
|
||||||
['type', 'status', 'color', 'length', 'length_unit'],
|
('Attributes', ('type', 'status', 'color', 'length', 'length_unit')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -736,11 +741,11 @@ class CableFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelFilterForm(CustomFieldModelFilterForm):
|
class PowerPanelFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
field_groups = (
|
fieldsets = (
|
||||||
('q', 'tag'),
|
(None, ('q', 'tag')),
|
||||||
('region_id', 'site_group_id', 'site_id', 'location_id')
|
('Location', ('region_id', 'site_group_id', 'site_id', 'location_id'))
|
||||||
)
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -773,14 +778,13 @@ class PowerPanelFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedFilterForm(CustomFieldModelFilterForm):
|
class PowerFeedFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = PowerFeed
|
model = PowerFeed
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id', 'power_panel_id', 'rack_id')),
|
||||||
['power_panel_id', 'rack_id'],
|
('Attributes', ('status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization')),
|
||||||
['status', 'type', 'supply', 'phase', 'voltage', 'amperage', 'max_utilization'],
|
)
|
||||||
]
|
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -855,11 +859,11 @@ class PowerFeedFilterForm(CustomFieldModelFilterForm):
|
|||||||
|
|
||||||
class ConsolePortFilterForm(DeviceComponentFilterForm):
|
class ConsolePortFilterForm(DeviceComponentFilterForm):
|
||||||
model = ConsolePort
|
model = ConsolePort
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type', 'speed'],
|
('Attributes', ('name', 'label', 'type', 'speed')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -875,11 +879,11 @@ class ConsolePortFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class ConsoleServerPortFilterForm(DeviceComponentFilterForm):
|
class ConsoleServerPortFilterForm(DeviceComponentFilterForm):
|
||||||
model = ConsoleServerPort
|
model = ConsoleServerPort
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type', 'speed'],
|
('Attributes', ('name', 'label', 'type', 'speed')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=ConsolePortTypeChoices,
|
choices=ConsolePortTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -895,11 +899,11 @@ class ConsoleServerPortFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class PowerPortFilterForm(DeviceComponentFilterForm):
|
class PowerPortFilterForm(DeviceComponentFilterForm):
|
||||||
model = PowerPort
|
model = PowerPort
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type'],
|
('Attributes', ('name', 'label', 'type')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=PowerPortTypeChoices,
|
choices=PowerPortTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -910,11 +914,11 @@ class PowerPortFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class PowerOutletFilterForm(DeviceComponentFilterForm):
|
class PowerOutletFilterForm(DeviceComponentFilterForm):
|
||||||
model = PowerOutlet
|
model = PowerOutlet
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type'],
|
('Attributes', ('name', 'label', 'type')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=PowerOutletTypeChoices,
|
choices=PowerOutletTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -925,13 +929,13 @@ class PowerOutletFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class InterfaceFilterForm(DeviceComponentFilterForm):
|
class InterfaceFilterForm(DeviceComponentFilterForm):
|
||||||
model = Interface
|
model = Interface
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only'],
|
('Attributes', ('name', 'label', 'kind', 'type', 'speed', 'duplex', 'enabled', 'mgmt_only')),
|
||||||
['vrf_id', 'mac_address', 'wwn'],
|
('Addressing', ('vrf_id', 'mac_address', 'wwn')),
|
||||||
['rf_role', 'rf_channel', 'rf_channel_width', 'tx_power'],
|
('Wireless', ('rf_role', 'rf_channel', 'rf_channel_width', 'tx_power')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
kind = forms.MultipleChoiceField(
|
kind = forms.MultipleChoiceField(
|
||||||
choices=InterfaceKindChoices,
|
choices=InterfaceKindChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -1008,11 +1012,11 @@ class InterfaceFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
|
|
||||||
class FrontPortFilterForm(DeviceComponentFilterForm):
|
class FrontPortFilterForm(DeviceComponentFilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type', 'color'],
|
('Attributes', ('name', 'label', 'type', 'color')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
model = FrontPort
|
model = FrontPort
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
@ -1027,11 +1031,11 @@ class FrontPortFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class RearPortFilterForm(DeviceComponentFilterForm):
|
class RearPortFilterForm(DeviceComponentFilterForm):
|
||||||
model = RearPort
|
model = RearPort
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'type', 'color'],
|
('Attributes', ('name', 'label', 'type', 'color')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
type = forms.MultipleChoiceField(
|
type = forms.MultipleChoiceField(
|
||||||
choices=PortTypeChoices,
|
choices=PortTypeChoices,
|
||||||
required=False,
|
required=False,
|
||||||
@ -1045,11 +1049,11 @@ class RearPortFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class ModuleBayFilterForm(DeviceComponentFilterForm):
|
class ModuleBayFilterForm(DeviceComponentFilterForm):
|
||||||
model = ModuleBay
|
model = ModuleBay
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'position'],
|
('Attributes', ('name', 'label', 'position')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
position = forms.CharField(
|
position = forms.CharField(
|
||||||
required=False
|
required=False
|
||||||
@ -1058,21 +1062,21 @@ class ModuleBayFilterForm(DeviceComponentFilterForm):
|
|||||||
|
|
||||||
class DeviceBayFilterForm(DeviceComponentFilterForm):
|
class DeviceBayFilterForm(DeviceComponentFilterForm):
|
||||||
model = DeviceBay
|
model = DeviceBay
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label'],
|
('Attributes', ('name', 'label')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemFilterForm(DeviceComponentFilterForm):
|
class InventoryItemFilterForm(DeviceComponentFilterForm):
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered'],
|
('Attributes', ('name', 'label', 'manufacturer_id', 'serial', 'asset_tag', 'discovered')),
|
||||||
['region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id'],
|
('Device', ('region_id', 'site_group_id', 'site_id', 'location_id', 'virtual_chassis_id', 'device_id')),
|
||||||
]
|
)
|
||||||
role_id = DynamicModelMultipleChoiceField(
|
role_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=InventoryItemRole.objects.all(),
|
queryset=InventoryItemRole.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1103,7 +1107,7 @@ class InventoryItemFilterForm(DeviceComponentFilterForm):
|
|||||||
# Device component roles
|
# Device component roles
|
||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemRoleFilterForm(CustomFieldModelFilterForm):
|
class InventoryItemRoleFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = InventoryItemRole
|
model = InventoryItemRole
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ from timezone_field import TimeZoneFormField
|
|||||||
from dcim.choices import *
|
from dcim.choices import *
|
||||||
from dcim.constants import *
|
from dcim.constants import *
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VRF
|
from ipam.models import ASN, IPAddress, VLAN, VLANGroup, VRF
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField,
|
APISelect, add_blank_choice, BootstrapMixin, ClearableFileInput, CommentField, ContentTypeChoiceField,
|
||||||
@ -72,7 +72,7 @@ Tagged (All): Implies all VLANs are available (w/optional untagged VLAN)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RegionForm(CustomFieldModelForm):
|
class RegionForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -90,7 +90,7 @@ class RegionForm(CustomFieldModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SiteGroupForm(CustomFieldModelForm):
|
class SiteGroupForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=SiteGroup.objects.all(),
|
queryset=SiteGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -108,7 +108,7 @@ class SiteGroupForm(CustomFieldModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SiteForm(TenancyForm, CustomFieldModelForm):
|
class SiteForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -134,19 +134,20 @@ class SiteForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Site', (
|
||||||
|
'name', 'slug', 'status', 'region', 'group', 'facility', 'asns', 'time_zone', 'description', 'tags',
|
||||||
|
)),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
('Contact Info', ('physical_address', 'shipping_address', 'latitude', 'longitude')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Site
|
model = Site
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
|
'name', 'slug', 'status', 'region', 'group', 'tenant_group', 'tenant', 'facility', 'asns', 'time_zone',
|
||||||
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
|
'description', 'physical_address', 'shipping_address', 'latitude', 'longitude', 'comments', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('Site', (
|
|
||||||
'name', 'slug', 'status', 'region', 'group', 'facility', 'asns', 'time_zone', 'description', 'tags',
|
|
||||||
)),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
('Contact Info', ('physical_address', 'shipping_address', 'latitude', 'longitude')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'physical_address': SmallTextarea(
|
'physical_address': SmallTextarea(
|
||||||
attrs={
|
attrs={
|
||||||
@ -173,7 +174,7 @@ class SiteForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LocationForm(TenancyForm, CustomFieldModelForm):
|
class LocationForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -208,20 +209,21 @@ class LocationForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Location', (
|
||||||
|
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tags',
|
||||||
|
)),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
|
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tenant_group', 'tenant', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('Location', (
|
|
||||||
'region', 'site_group', 'site', 'parent', 'name', 'slug', 'description', 'tags',
|
|
||||||
)),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RackRoleForm(CustomFieldModelForm):
|
class RackRoleForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -235,7 +237,7 @@ class RackRoleForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RackForm(TenancyForm, CustomFieldModelForm):
|
class RackForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -295,7 +297,7 @@ class RackForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RackReservationForm(TenancyForm, CustomFieldModelForm):
|
class RackReservationForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -347,19 +349,20 @@ class RackReservationForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Reservation', ('region', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RackReservation
|
model = RackReservation
|
||||||
fields = [
|
fields = [
|
||||||
'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant',
|
'region', 'site_group', 'site', 'location', 'rack', 'units', 'user', 'tenant_group', 'tenant',
|
||||||
'description', 'tags',
|
'description', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Reservation', ('region', 'site', 'location', 'rack', 'units', 'user', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerForm(CustomFieldModelForm):
|
class ManufacturerForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -373,7 +376,7 @@ class ManufacturerForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceTypeForm(CustomFieldModelForm):
|
class DeviceTypeForm(NetBoxModelForm):
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all()
|
queryset=Manufacturer.objects.all()
|
||||||
)
|
)
|
||||||
@ -386,21 +389,22 @@ class DeviceTypeForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Device Type', (
|
||||||
|
'manufacturer', 'model', 'slug', 'part_number', 'tags',
|
||||||
|
)),
|
||||||
|
('Chassis', (
|
||||||
|
'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
|
)),
|
||||||
|
('Images', ('front_image', 'rear_image')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = DeviceType
|
model = DeviceType
|
||||||
fields = [
|
fields = [
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
'manufacturer', 'model', 'slug', 'part_number', 'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
||||||
'front_image', 'rear_image', 'comments', 'tags',
|
'front_image', 'rear_image', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Device Type', (
|
|
||||||
'manufacturer', 'model', 'slug', 'part_number', 'tags',
|
|
||||||
)),
|
|
||||||
('Chassis', (
|
|
||||||
'u_height', 'is_full_depth', 'subdevice_role', 'airflow',
|
|
||||||
)),
|
|
||||||
('Images', ('front_image', 'rear_image')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'subdevice_role': StaticSelect(),
|
'subdevice_role': StaticSelect(),
|
||||||
'front_image': ClearableFileInput(attrs={
|
'front_image': ClearableFileInput(attrs={
|
||||||
@ -412,7 +416,7 @@ class DeviceTypeForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ModuleTypeForm(CustomFieldModelForm):
|
class ModuleTypeForm(NetBoxModelForm):
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all()
|
queryset=Manufacturer.objects.all()
|
||||||
)
|
)
|
||||||
@ -429,7 +433,7 @@ class ModuleTypeForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceRoleForm(CustomFieldModelForm):
|
class DeviceRoleForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -443,7 +447,7 @@ class DeviceRoleForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PlatformForm(CustomFieldModelForm):
|
class PlatformForm(NetBoxModelForm):
|
||||||
manufacturer = DynamicModelChoiceField(
|
manufacturer = DynamicModelChoiceField(
|
||||||
queryset=Manufacturer.objects.all(),
|
queryset=Manufacturer.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -466,7 +470,7 @@ class PlatformForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeviceForm(TenancyForm, CustomFieldModelForm):
|
class DeviceForm(TenancyForm, NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -648,7 +652,7 @@ class DeviceForm(TenancyForm, CustomFieldModelForm):
|
|||||||
self.fields['position'].widget.choices = [(position, f'U{position}')]
|
self.fields['position'].widget.choices = [(position, f'U{position}')]
|
||||||
|
|
||||||
|
|
||||||
class ModuleForm(CustomFieldModelForm):
|
class ModuleForm(NetBoxModelForm):
|
||||||
device = DynamicModelChoiceField(
|
device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -688,7 +692,7 @@ class ModuleForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CableForm(TenancyForm, CustomFieldModelForm):
|
class CableForm(TenancyForm, NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -711,7 +715,7 @@ class CableForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PowerPanelForm(CustomFieldModelForm):
|
class PowerPanelForm(NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -745,17 +749,18 @@ class PowerPanelForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'tags')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerPanel
|
model = PowerPanel
|
||||||
fields = [
|
fields = [
|
||||||
'region', 'site_group', 'site', 'location', 'name', 'tags',
|
'region', 'site_group', 'site', 'location', 'name', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Power Panel', ('region', 'site_group', 'site', 'location', 'name', 'tags')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PowerFeedForm(CustomFieldModelForm):
|
class PowerFeedForm(NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -800,17 +805,18 @@ class PowerFeedForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Power Panel', ('region', 'site', 'power_panel')),
|
||||||
|
('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
|
||||||
|
('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PowerFeed
|
model = PowerFeed
|
||||||
fields = [
|
fields = [
|
||||||
'region', 'site_group', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply',
|
'region', 'site_group', 'site', 'power_panel', 'rack', 'name', 'status', 'type', 'mark_connected', 'supply',
|
||||||
'phase', 'voltage', 'amperage', 'max_utilization', 'comments', 'tags',
|
'phase', 'voltage', 'amperage', 'max_utilization', 'comments', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Power Panel', ('region', 'site', 'power_panel')),
|
|
||||||
('Power Feed', ('rack', 'name', 'status', 'type', 'mark_connected', 'tags')),
|
|
||||||
('Characteristics', ('supply', 'voltage', 'amperage', 'phase', 'max_utilization')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect(),
|
'status': StaticSelect(),
|
||||||
'type': StaticSelect(),
|
'type': StaticSelect(),
|
||||||
@ -823,7 +829,7 @@ class PowerFeedForm(CustomFieldModelForm):
|
|||||||
# Virtual chassis
|
# Virtual chassis
|
||||||
#
|
#
|
||||||
|
|
||||||
class VirtualChassisForm(CustomFieldModelForm):
|
class VirtualChassisForm(NetBoxModelForm):
|
||||||
master = forms.ModelChoiceField(
|
master = forms.ModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1101,16 +1107,17 @@ class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
widget=forms.HiddenInput
|
widget=forms.HiddenInput
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Inventory Item', ('device_type', 'parent', 'name', 'label', 'role', 'description')),
|
||||||
|
('Hardware', ('manufacturer', 'part_id')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItemTemplate
|
model = InventoryItemTemplate
|
||||||
fields = [
|
fields = [
|
||||||
'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description',
|
'device_type', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'description',
|
||||||
'component_type', 'component_id',
|
'component_type', 'component_id',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Inventory Item', ('device_type', 'parent', 'name', 'label', 'role', 'description')),
|
|
||||||
('Hardware', ('manufacturer', 'part_id')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'device_type': forms.HiddenInput(),
|
'device_type': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
@ -1120,7 +1127,7 @@ class InventoryItemTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
# Device components
|
# Device components
|
||||||
#
|
#
|
||||||
|
|
||||||
class ConsolePortForm(CustomFieldModelForm):
|
class ConsolePortForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1138,7 +1145,7 @@ class ConsolePortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ConsoleServerPortForm(CustomFieldModelForm):
|
class ConsoleServerPortForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1156,7 +1163,7 @@ class ConsoleServerPortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PowerPortForm(CustomFieldModelForm):
|
class PowerPortForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1174,7 +1181,7 @@ class PowerPortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PowerOutletForm(CustomFieldModelForm):
|
class PowerOutletForm(NetBoxModelForm):
|
||||||
power_port = DynamicModelChoiceField(
|
power_port = DynamicModelChoiceField(
|
||||||
queryset=PowerPort.objects.all(),
|
queryset=PowerPort.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1199,7 +1206,7 @@ class PowerOutletForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
class InterfaceForm(InterfaceCommonForm, NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=Interface.objects.all(),
|
queryset=Interface.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1271,6 +1278,17 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Interface', ('device', 'name', 'type', 'speed', 'duplex', 'label', 'description', 'tags')),
|
||||||
|
('Addressing', ('vrf', 'mac_address', 'wwn')),
|
||||||
|
('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')),
|
||||||
|
('Related Interfaces', ('parent', 'bridge', 'lag')),
|
||||||
|
('802.1Q Switching', ('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans')),
|
||||||
|
('Wireless', (
|
||||||
|
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group', 'wireless_lans',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = [
|
fields = [
|
||||||
@ -1278,17 +1296,6 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
|||||||
'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
'mgmt_only', 'mark_connected', 'description', 'mode', 'rf_role', 'rf_channel', 'rf_channel_frequency',
|
||||||
'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
'rf_channel_width', 'tx_power', 'wireless_lans', 'untagged_vlan', 'tagged_vlans', 'vrf', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Interface', ('device', 'name', 'type', 'speed', 'duplex', 'label', 'description', 'tags')),
|
|
||||||
('Addressing', ('vrf', 'mac_address', 'wwn')),
|
|
||||||
('Operation', ('mtu', 'tx_power', 'enabled', 'mgmt_only', 'mark_connected')),
|
|
||||||
('Related Interfaces', ('parent', 'bridge', 'lag')),
|
|
||||||
('802.1Q Switching', ('mode', 'vlan_group', 'untagged_vlan', 'tagged_vlans')),
|
|
||||||
('Wireless', (
|
|
||||||
'rf_role', 'rf_channel', 'rf_channel_frequency', 'rf_channel_width', 'wireless_lan_group',
|
|
||||||
'wireless_lans',
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'device': forms.HiddenInput(),
|
'device': forms.HiddenInput(),
|
||||||
'type': StaticSelect(),
|
'type': StaticSelect(),
|
||||||
@ -1308,7 +1315,7 @@ class InterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class FrontPortForm(CustomFieldModelForm):
|
class FrontPortForm(NetBoxModelForm):
|
||||||
rear_port = DynamicModelChoiceField(
|
rear_port = DynamicModelChoiceField(
|
||||||
queryset=RearPort.objects.all(),
|
queryset=RearPort.objects.all(),
|
||||||
query_params={
|
query_params={
|
||||||
@ -1332,7 +1339,7 @@ class FrontPortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RearPortForm(CustomFieldModelForm):
|
class RearPortForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1349,7 +1356,7 @@ class RearPortForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ModuleBayForm(CustomFieldModelForm):
|
class ModuleBayForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1365,7 +1372,7 @@ class ModuleBayForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DeviceBayForm(CustomFieldModelForm):
|
class DeviceBayForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -1401,7 +1408,7 @@ class PopulateDeviceBayForm(BootstrapMixin, forms.Form):
|
|||||||
).exclude(pk=device_bay.device.pk)
|
).exclude(pk=device_bay.device.pk)
|
||||||
|
|
||||||
|
|
||||||
class InventoryItemForm(CustomFieldModelForm):
|
class InventoryItemForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=InventoryItem.objects.all(),
|
queryset=InventoryItem.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -1432,16 +1439,17 @@ class InventoryItemForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Inventory Item', ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
|
||||||
|
('Hardware', ('manufacturer', 'part_id', 'serial', 'asset_tag')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = InventoryItem
|
model = InventoryItem
|
||||||
fields = [
|
fields = [
|
||||||
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
'device', 'parent', 'name', 'label', 'role', 'manufacturer', 'part_id', 'serial', 'asset_tag',
|
||||||
'description', 'component_type', 'component_id', 'tags',
|
'description', 'component_type', 'component_id', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Inventory Item', ('device', 'parent', 'name', 'label', 'role', 'description', 'tags')),
|
|
||||||
('Hardware', ('manufacturer', 'part_id', 'serial', 'asset_tag')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'device': forms.HiddenInput(),
|
'device': forms.HiddenInput(),
|
||||||
}
|
}
|
||||||
@ -1451,7 +1459,7 @@ class InventoryItemForm(CustomFieldModelForm):
|
|||||||
# Device component roles
|
# Device component roles
|
||||||
#
|
#
|
||||||
|
|
||||||
class InventoryItemRoleForm(CustomFieldModelForm):
|
class InventoryItemRoleForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from dcim.models import *
|
from dcim.models import *
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField,
|
BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, ExpandableNameField,
|
||||||
)
|
)
|
||||||
@ -149,7 +149,7 @@ class FrontPortCreateForm(DeviceComponentCreateForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VirtualChassisCreateForm(CustomFieldModelForm):
|
class VirtualChassisCreateForm(NetBoxModelForm):
|
||||||
region = DynamicModelChoiceField(
|
region = DynamicModelChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -33,8 +33,7 @@ class CustomFieldBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = []
|
|
||||||
|
|
||||||
|
|
||||||
class CustomLinkBulkEditForm(BulkEditForm):
|
class CustomLinkBulkEditForm(BulkEditForm):
|
||||||
@ -64,9 +63,6 @@ class CustomLinkBulkEditForm(BulkEditForm):
|
|||||||
widget=StaticSelect()
|
widget=StaticSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
nullable_fields = []
|
|
||||||
|
|
||||||
|
|
||||||
class ExportTemplateBulkEditForm(BulkEditForm):
|
class ExportTemplateBulkEditForm(BulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
@ -95,8 +91,7 @@ class ExportTemplateBulkEditForm(BulkEditForm):
|
|||||||
widget=BulkEditNullBooleanSelect()
|
widget=BulkEditNullBooleanSelect()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description', 'mime_type', 'file_extension')
|
||||||
nullable_fields = ['description', 'mime_type', 'file_extension']
|
|
||||||
|
|
||||||
|
|
||||||
class WebhookBulkEditForm(BulkEditForm):
|
class WebhookBulkEditForm(BulkEditForm):
|
||||||
@ -138,8 +133,7 @@ class WebhookBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('secret', 'conditions', 'ca_file_path')
|
||||||
nullable_fields = ['secret', 'conditions', 'ca_file_path']
|
|
||||||
|
|
||||||
|
|
||||||
class TagBulkEditForm(BulkEditForm):
|
class TagBulkEditForm(BulkEditForm):
|
||||||
@ -155,8 +149,7 @@ class TagBulkEditForm(BulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextBulkEditForm(BulkEditForm):
|
class ConfigContextBulkEditForm(BulkEditForm):
|
||||||
@ -177,10 +170,7 @@ class ConfigContextBulkEditForm(BulkEditForm):
|
|||||||
max_length=100
|
max_length=100
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = [
|
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class JournalEntryBulkEditForm(BulkEditForm):
|
class JournalEntryBulkEditForm(BulkEditForm):
|
||||||
@ -196,6 +186,3 @@ class JournalEntryBulkEditForm(BulkEditForm):
|
|||||||
required=False,
|
required=False,
|
||||||
widget=forms.Textarea()
|
widget=forms.Textarea()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
nullable_fields = []
|
|
||||||
|
@ -1,16 +1,8 @@
|
|||||||
from django import forms
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from extras.choices import *
|
|
||||||
from extras.models import *
|
from extras.models import *
|
||||||
from utilities.forms import BootstrapMixin, BulkEditBaseForm, CSVModelForm
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'CustomFieldModelCSVForm',
|
|
||||||
'CustomFieldModelBulkEditForm',
|
|
||||||
'CustomFieldModelFilterForm',
|
|
||||||
'CustomFieldModelForm',
|
|
||||||
'CustomFieldsMixin',
|
'CustomFieldsMixin',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,76 +42,3 @@ class CustomFieldsMixin:
|
|||||||
|
|
||||||
# Annotate the field in the list of CustomField form fields
|
# Annotate the field in the list of CustomField form fields
|
||||||
self.custom_fields[field_name] = customfield
|
self.custom_fields[field_name] = customfield
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldModelForm(BootstrapMixin, CustomFieldsMixin, forms.ModelForm):
|
|
||||||
"""
|
|
||||||
Extend ModelForm to include custom field support.
|
|
||||||
"""
|
|
||||||
def _get_content_type(self):
|
|
||||||
return ContentType.objects.get_for_model(self._meta.model)
|
|
||||||
|
|
||||||
def _get_form_field(self, customfield):
|
|
||||||
if self.instance.pk:
|
|
||||||
form_field = customfield.to_form_field(set_initial=False)
|
|
||||||
form_field.initial = self.instance.custom_field_data.get(customfield.name, None)
|
|
||||||
return form_field
|
|
||||||
|
|
||||||
return customfield.to_form_field()
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
|
|
||||||
# Save custom field data on instance
|
|
||||||
for cf_name, customfield in self.custom_fields.items():
|
|
||||||
key = cf_name[3:] # Strip "cf_" from field name
|
|
||||||
value = self.cleaned_data.get(cf_name)
|
|
||||||
|
|
||||||
# Convert "empty" values to null
|
|
||||||
if value in self.fields[cf_name].empty_values:
|
|
||||||
self.instance.custom_field_data[key] = None
|
|
||||||
else:
|
|
||||||
self.instance.custom_field_data[key] = customfield.serialize(value)
|
|
||||||
|
|
||||||
return super().clean()
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldModelCSVForm(CSVModelForm, CustomFieldModelForm):
|
|
||||||
|
|
||||||
def _get_form_field(self, customfield):
|
|
||||||
return customfield.to_form_field(for_csv_import=True)
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldModelBulkEditForm(BootstrapMixin, CustomFieldsMixin, BulkEditBaseForm):
|
|
||||||
|
|
||||||
def _get_form_field(self, customfield):
|
|
||||||
return customfield.to_form_field(set_initial=False, enforce_required=False)
|
|
||||||
|
|
||||||
def _append_customfield_fields(self):
|
|
||||||
"""
|
|
||||||
Append form fields for all CustomFields assigned to this object type.
|
|
||||||
"""
|
|
||||||
for customfield in self._get_custom_fields(self._get_content_type()):
|
|
||||||
# Annotate non-required custom fields as nullable
|
|
||||||
if not customfield.required:
|
|
||||||
self.nullable_fields.append(customfield.name)
|
|
||||||
|
|
||||||
self.fields[customfield.name] = self._get_form_field(customfield)
|
|
||||||
|
|
||||||
# Annotate the field in the list of CustomField form fields
|
|
||||||
self.custom_fields[customfield.name] = customfield
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFieldModelFilterForm(BootstrapMixin, CustomFieldsMixin, forms.Form):
|
|
||||||
q = forms.CharField(
|
|
||||||
required=False,
|
|
||||||
label='Search'
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_custom_fields(self, content_type):
|
|
||||||
return CustomField.objects.filter(content_types=content_type).exclude(
|
|
||||||
Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) |
|
|
||||||
Q(type=CustomFieldTypeChoices.TYPE_JSON)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_form_field(self, customfield):
|
|
||||||
return customfield.to_form_field(set_initial=False, enforce_required=False)
|
|
||||||
|
@ -28,11 +28,10 @@ __all__ = (
|
|||||||
|
|
||||||
|
|
||||||
class CustomFieldFilterForm(FilterForm):
|
class CustomFieldFilterForm(FilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['type', 'content_types'],
|
('Attributes', ('type', 'content_types', 'weight', 'required')),
|
||||||
['weight', 'required'],
|
)
|
||||||
]
|
|
||||||
content_types = ContentTypeMultipleChoiceField(
|
content_types = ContentTypeMultipleChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('custom_fields'),
|
limit_choices_to=FeatureQuery('custom_fields'),
|
||||||
@ -56,10 +55,10 @@ class CustomFieldFilterForm(FilterForm):
|
|||||||
|
|
||||||
|
|
||||||
class CustomLinkFilterForm(FilterForm):
|
class CustomLinkFilterForm(FilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['content_type', 'enabled', 'new_window', 'weight'],
|
('Attributes', ('content_type', 'enabled', 'new_window', 'weight')),
|
||||||
]
|
)
|
||||||
content_type = ContentTypeChoiceField(
|
content_type = ContentTypeChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('custom_fields'),
|
limit_choices_to=FeatureQuery('custom_fields'),
|
||||||
@ -83,10 +82,10 @@ class CustomLinkFilterForm(FilterForm):
|
|||||||
|
|
||||||
|
|
||||||
class ExportTemplateFilterForm(FilterForm):
|
class ExportTemplateFilterForm(FilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['content_type', 'mime_type', 'file_extension', 'as_attachment'],
|
('Attributes', ('content_type', 'mime_type', 'file_extension', 'as_attachment')),
|
||||||
]
|
)
|
||||||
content_type = ContentTypeChoiceField(
|
content_type = ContentTypeChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('custom_fields'),
|
limit_choices_to=FeatureQuery('custom_fields'),
|
||||||
@ -108,11 +107,11 @@ class ExportTemplateFilterForm(FilterForm):
|
|||||||
|
|
||||||
|
|
||||||
class WebhookFilterForm(FilterForm):
|
class WebhookFilterForm(FilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['content_types', 'http_method', 'enabled'],
|
('Attributes', ('content_types', 'http_method', 'enabled')),
|
||||||
['type_create', 'type_update', 'type_delete'],
|
('Events', ('type_create', 'type_update', 'type_delete')),
|
||||||
]
|
)
|
||||||
content_types = ContentTypeMultipleChoiceField(
|
content_types = ContentTypeMultipleChoiceField(
|
||||||
queryset=ContentType.objects.all(),
|
queryset=ContentType.objects.all(),
|
||||||
limit_choices_to=FeatureQuery('custom_fields'),
|
limit_choices_to=FeatureQuery('custom_fields'),
|
||||||
@ -160,13 +159,13 @@ class TagFilterForm(FilterForm):
|
|||||||
|
|
||||||
|
|
||||||
class ConfigContextFilterForm(FilterForm):
|
class ConfigContextFilterForm(FilterForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['device_type_id', 'platform_id', 'role_id'],
|
('Device', ('device_type_id', 'platform_id', 'role_id')),
|
||||||
['cluster_type_id', 'cluster_group_id', 'cluster_id'],
|
('Cluster', ('cluster_type_id', 'cluster_group_id', 'cluster_id')),
|
||||||
['tenant_group_id', 'tenant_id']
|
('Tenant', ('tenant_group_id', 'tenant_id'))
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -243,11 +242,11 @@ class LocalConfigContextFilterForm(forms.Form):
|
|||||||
|
|
||||||
class JournalEntryFilterForm(FilterForm):
|
class JournalEntryFilterForm(FilterForm):
|
||||||
model = JournalEntry
|
model = JournalEntry
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['created_before', 'created_after', 'created_by_id'],
|
('Creation', ('created_before', 'created_after', 'created_by_id')),
|
||||||
['assigned_object_type_id', 'kind']
|
('Attributes', ('assigned_object_type_id', 'kind'))
|
||||||
]
|
)
|
||||||
created_after = forms.DateTimeField(
|
created_after = forms.DateTimeField(
|
||||||
required=False,
|
required=False,
|
||||||
label=_('After'),
|
label=_('After'),
|
||||||
@ -283,11 +282,11 @@ class JournalEntryFilterForm(FilterForm):
|
|||||||
|
|
||||||
class ObjectChangeFilterForm(FilterForm):
|
class ObjectChangeFilterForm(FilterForm):
|
||||||
model = ObjectChange
|
model = ObjectChange
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q',)),
|
||||||
['time_before', 'time_after', 'action'],
|
('Time', ('time_before', 'time_after')),
|
||||||
['user_id', 'changed_object_type_id'],
|
('Attributes', ('action', 'user_id', 'changed_object_type_id')),
|
||||||
]
|
)
|
||||||
time_after = forms.DateTimeField(
|
time_after = forms.DateTimeField(
|
||||||
required=False,
|
required=False,
|
||||||
label=_('After'),
|
label=_('After'),
|
||||||
|
@ -13,7 +13,6 @@ from utilities.forms import (
|
|||||||
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'AddRemoveTagsForm',
|
|
||||||
'ConfigContextForm',
|
'ConfigContextForm',
|
||||||
'CustomFieldForm',
|
'CustomFieldForm',
|
||||||
'CustomLinkForm',
|
'CustomLinkForm',
|
||||||
@ -31,16 +30,17 @@ class CustomFieldForm(BootstrapMixin, forms.ModelForm):
|
|||||||
limit_choices_to=FeatureQuery('custom_fields')
|
limit_choices_to=FeatureQuery('custom_fields')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Custom Field', ('name', 'label', 'type', 'object_type', 'weight', 'required', 'description')),
|
||||||
|
('Assigned Models', ('content_types',)),
|
||||||
|
('Behavior', ('filter_logic',)),
|
||||||
|
('Values', ('default', 'choices')),
|
||||||
|
('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomField
|
model = CustomField
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
fieldsets = (
|
|
||||||
('Custom Field', ('name', 'label', 'type', 'object_type', 'weight', 'required', 'description')),
|
|
||||||
('Assigned Models', ('content_types',)),
|
|
||||||
('Behavior', ('filter_logic',)),
|
|
||||||
('Values', ('default', 'choices')),
|
|
||||||
('Validation', ('validation_minimum', 'validation_maximum', 'validation_regex')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'type': StaticSelect(),
|
'type': StaticSelect(),
|
||||||
'filter_logic': StaticSelect(),
|
'filter_logic': StaticSelect(),
|
||||||
@ -53,13 +53,14 @@ class CustomLinkForm(BootstrapMixin, forms.ModelForm):
|
|||||||
limit_choices_to=FeatureQuery('custom_links')
|
limit_choices_to=FeatureQuery('custom_links')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'enabled', 'new_window')),
|
||||||
|
('Templates', ('link_text', 'link_url')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomLink
|
model = CustomLink
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
fieldsets = (
|
|
||||||
('Custom Link', ('name', 'content_type', 'weight', 'group_name', 'button_class', 'enabled', 'new_window')),
|
|
||||||
('Templates', ('link_text', 'link_url')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'button_class': StaticSelect(),
|
'button_class': StaticSelect(),
|
||||||
'link_text': forms.Textarea(attrs={'class': 'font-monospace'}),
|
'link_text': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||||
@ -78,14 +79,15 @@ class ExportTemplateForm(BootstrapMixin, forms.ModelForm):
|
|||||||
limit_choices_to=FeatureQuery('export_templates')
|
limit_choices_to=FeatureQuery('export_templates')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Export Template', ('name', 'content_type', 'description')),
|
||||||
|
('Template', ('template_code',)),
|
||||||
|
('Rendering', ('mime_type', 'file_extension', 'as_attachment')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExportTemplate
|
model = ExportTemplate
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
fieldsets = (
|
|
||||||
('Export Template', ('name', 'content_type', 'description')),
|
|
||||||
('Template', ('template_code',)),
|
|
||||||
('Rendering', ('mime_type', 'file_extension', 'as_attachment')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'template_code': forms.Textarea(attrs={'class': 'font-monospace'}),
|
'template_code': forms.Textarea(attrs={'class': 'font-monospace'}),
|
||||||
}
|
}
|
||||||
@ -97,18 +99,19 @@ class WebhookForm(BootstrapMixin, forms.ModelForm):
|
|||||||
limit_choices_to=FeatureQuery('webhooks')
|
limit_choices_to=FeatureQuery('webhooks')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Webhook', ('name', 'content_types', 'enabled')),
|
||||||
|
('Events', ('type_create', 'type_update', 'type_delete')),
|
||||||
|
('HTTP Request', (
|
||||||
|
'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret',
|
||||||
|
)),
|
||||||
|
('Conditions', ('conditions',)),
|
||||||
|
('SSL', ('ssl_verification', 'ca_file_path')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Webhook
|
model = Webhook
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
fieldsets = (
|
|
||||||
('Webhook', ('name', 'content_types', 'enabled')),
|
|
||||||
('Events', ('type_create', 'type_update', 'type_delete')),
|
|
||||||
('HTTP Request', (
|
|
||||||
'payload_url', 'http_method', 'http_content_type', 'additional_headers', 'body_template', 'secret',
|
|
||||||
)),
|
|
||||||
('Conditions', ('conditions',)),
|
|
||||||
('SSL', ('ssl_verification', 'ca_file_path')),
|
|
||||||
)
|
|
||||||
labels = {
|
labels = {
|
||||||
'type_create': 'Creations',
|
'type_create': 'Creations',
|
||||||
'type_update': 'Updates',
|
'type_update': 'Updates',
|
||||||
@ -124,30 +127,15 @@ class WebhookForm(BootstrapMixin, forms.ModelForm):
|
|||||||
class TagForm(BootstrapMixin, forms.ModelForm):
|
class TagForm(BootstrapMixin, forms.ModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Tag', ('name', 'slug', 'color', 'description')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug', 'color', 'description'
|
'name', 'slug', 'color', 'description'
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Tag', ('name', 'slug', 'color', 'description')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AddRemoveTagsForm(forms.Form):
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Add add/remove tags fields
|
|
||||||
self.fields['add_tags'] = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Tag.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
self.fields['remove_tags'] = DynamicModelMultipleChoiceField(
|
|
||||||
queryset=Tag.objects.all(),
|
|
||||||
required=False
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
class ConfigContextForm(BootstrapMixin, forms.ModelForm):
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from dcim.models import Region, Site, SiteGroup
|
from dcim.models import Region, Site, SiteGroup
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, BulkEditNullBooleanSelect, DatePicker, DynamicModelChoiceField, NumericArrayField, StaticSelect,
|
add_blank_choice, BulkEditNullBooleanSelect, DatePicker, DynamicModelChoiceField, NumericArrayField, StaticSelect,
|
||||||
@ -30,7 +30,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VRFBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VRFBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -49,13 +49,10 @@ class VRFBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('tenant', 'description')
|
||||||
nullable_fields = [
|
|
||||||
'tenant', 'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RouteTargetBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RouteTargetBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=RouteTarget.objects.all(),
|
queryset=RouteTarget.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -69,13 +66,10 @@ class RouteTargetBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('tenant', 'description')
|
||||||
nullable_fields = [
|
|
||||||
'tenant', 'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class RIRBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RIRBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -89,11 +83,10 @@ class RIRBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('is_private', 'description')
|
||||||
nullable_fields = ['is_private', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class ASNBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ASNBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ASN.objects.all(),
|
queryset=ASN.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -116,16 +109,10 @@ class ASNBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('date_added', 'description')
|
||||||
nullable_fields = [
|
|
||||||
'date_added', 'description',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'date_added': DatePicker(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AggregateBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class AggregateBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Aggregate.objects.all(),
|
queryset=Aggregate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -147,16 +134,10 @@ class AggregateBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('date_added', 'description')
|
||||||
nullable_fields = [
|
|
||||||
'date_added', 'description',
|
|
||||||
]
|
|
||||||
widgets = {
|
|
||||||
'date_added': DatePicker(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class RoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Role.objects.all(),
|
queryset=Role.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -169,11 +150,10 @@ class RoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class PrefixBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class PrefixBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Prefix.objects.all(),
|
queryset=Prefix.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -232,13 +212,12 @@ class PrefixBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'site', 'vrf', 'tenant', 'role', 'description',
|
||||||
'site', 'vrf', 'tenant', 'role', 'description',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IPRangeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class IPRangeBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=IPRange.objects.all(),
|
queryset=IPRange.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -266,13 +245,12 @@ class IPRangeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'vrf', 'tenant', 'role', 'description',
|
||||||
'vrf', 'tenant', 'role', 'description',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IPAddressBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class IPAddressBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=IPAddress.objects.all(),
|
queryset=IPAddress.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -311,13 +289,12 @@ class IPAddressBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'vrf', 'role', 'tenant', 'dns_name', 'description',
|
||||||
'vrf', 'role', 'tenant', 'dns_name', 'description',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class FHRPGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class FHRPGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=FHRPGroup.objects.all(),
|
queryset=FHRPGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -348,11 +325,10 @@ class FHRPGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('auth_type', 'auth_key', 'description')
|
||||||
nullable_fields = ['auth_type', 'auth_key', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VLANGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VLANGroup.objects.all(),
|
queryset=VLANGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -378,11 +354,10 @@ class VLANGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('site', 'description')
|
||||||
nullable_fields = ['site', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class VLANBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VLANBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VLAN.objects.all(),
|
queryset=VLAN.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -428,13 +403,12 @@ class VLANBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'site', 'group', 'tenant', 'role', 'description',
|
||||||
'site', 'group', 'tenant', 'role', 'description',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceTemplateBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ServiceTemplateBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ServiceTemplate.objects.all(),
|
queryset=ServiceTemplate.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -456,10 +430,7 @@ class ServiceTemplateBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditFor
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = [
|
|
||||||
'description',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceBulkEditForm(ServiceTemplateBulkEditForm):
|
class ServiceBulkEditForm(ServiceTemplateBulkEditForm):
|
||||||
|
@ -2,10 +2,10 @@ from django import forms
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Site
|
from dcim.models import Device, Interface, Site
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVContentTypeField, CSVModelChoiceField, SlugField
|
||||||
from virtualization.models import VirtualMachine, VMInterface
|
from virtualization.models import VirtualMachine, VMInterface
|
||||||
@ -28,7 +28,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VRFCSVForm(CustomFieldModelCSVForm):
|
class VRFCSVForm(NetBoxModelCSVForm):
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -41,7 +41,7 @@ class VRFCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'rd', 'tenant', 'enforce_unique', 'description')
|
fields = ('name', 'rd', 'tenant', 'enforce_unique', 'description')
|
||||||
|
|
||||||
|
|
||||||
class RouteTargetCSVForm(CustomFieldModelCSVForm):
|
class RouteTargetCSVForm(NetBoxModelCSVForm):
|
||||||
tenant = CSVModelChoiceField(
|
tenant = CSVModelChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -54,7 +54,7 @@ class RouteTargetCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'description', 'tenant')
|
fields = ('name', 'description', 'tenant')
|
||||||
|
|
||||||
|
|
||||||
class RIRCSVForm(CustomFieldModelCSVForm):
|
class RIRCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -65,7 +65,7 @@ class RIRCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AggregateCSVForm(CustomFieldModelCSVForm):
|
class AggregateCSVForm(NetBoxModelCSVForm):
|
||||||
rir = CSVModelChoiceField(
|
rir = CSVModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -83,7 +83,7 @@ class AggregateCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('prefix', 'rir', 'tenant', 'date_added', 'description')
|
fields = ('prefix', 'rir', 'tenant', 'date_added', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ASNCSVForm(CustomFieldModelCSVForm):
|
class ASNCSVForm(NetBoxModelCSVForm):
|
||||||
rir = CSVModelChoiceField(
|
rir = CSVModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -102,7 +102,7 @@ class ASNCSVForm(CustomFieldModelCSVForm):
|
|||||||
help_texts = {}
|
help_texts = {}
|
||||||
|
|
||||||
|
|
||||||
class RoleCSVForm(CustomFieldModelCSVForm):
|
class RoleCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -110,7 +110,7 @@ class RoleCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'weight', 'description')
|
fields = ('name', 'slug', 'weight', 'description')
|
||||||
|
|
||||||
|
|
||||||
class PrefixCSVForm(CustomFieldModelCSVForm):
|
class PrefixCSVForm(NetBoxModelCSVForm):
|
||||||
vrf = CSVModelChoiceField(
|
vrf = CSVModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -174,7 +174,7 @@ class PrefixCSVForm(CustomFieldModelCSVForm):
|
|||||||
self.fields['vlan'].queryset = self.fields['vlan'].queryset.filter(**params)
|
self.fields['vlan'].queryset = self.fields['vlan'].queryset.filter(**params)
|
||||||
|
|
||||||
|
|
||||||
class IPRangeCSVForm(CustomFieldModelCSVForm):
|
class IPRangeCSVForm(NetBoxModelCSVForm):
|
||||||
vrf = CSVModelChoiceField(
|
vrf = CSVModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -205,7 +205,7 @@ class IPRangeCSVForm(CustomFieldModelCSVForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class IPAddressCSVForm(CustomFieldModelCSVForm):
|
class IPAddressCSVForm(NetBoxModelCSVForm):
|
||||||
vrf = CSVModelChoiceField(
|
vrf = CSVModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -312,7 +312,7 @@ class IPAddressCSVForm(CustomFieldModelCSVForm):
|
|||||||
return ipaddress
|
return ipaddress
|
||||||
|
|
||||||
|
|
||||||
class FHRPGroupCSVForm(CustomFieldModelCSVForm):
|
class FHRPGroupCSVForm(NetBoxModelCSVForm):
|
||||||
protocol = CSVChoiceField(
|
protocol = CSVChoiceField(
|
||||||
choices=FHRPGroupProtocolChoices
|
choices=FHRPGroupProtocolChoices
|
||||||
)
|
)
|
||||||
@ -326,7 +326,7 @@ class FHRPGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'description')
|
fields = ('protocol', 'group_id', 'auth_type', 'auth_key', 'description')
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupCSVForm(CustomFieldModelCSVForm):
|
class VLANGroupCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
scope_type = CSVContentTypeField(
|
scope_type = CSVContentTypeField(
|
||||||
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
|
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
|
||||||
@ -354,7 +354,7 @@ class VLANGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class VLANCSVForm(CustomFieldModelCSVForm):
|
class VLANCSVForm(NetBoxModelCSVForm):
|
||||||
site = CSVModelChoiceField(
|
site = CSVModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -393,7 +393,7 @@ class VLANCSVForm(CustomFieldModelCSVForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ServiceTemplateCSVForm(CustomFieldModelCSVForm):
|
class ServiceTemplateCSVForm(NetBoxModelCSVForm):
|
||||||
protocol = CSVChoiceField(
|
protocol = CSVChoiceField(
|
||||||
choices=ServiceProtocolChoices,
|
choices=ServiceProtocolChoices,
|
||||||
help_text='IP protocol'
|
help_text='IP protocol'
|
||||||
@ -404,7 +404,7 @@ class ServiceTemplateCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'protocol', 'ports', 'description')
|
fields = ('name', 'protocol', 'ports', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ServiceCSVForm(CustomFieldModelCSVForm):
|
class ServiceCSVForm(NetBoxModelCSVForm):
|
||||||
device = CSVModelChoiceField(
|
device = CSVModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -2,11 +2,11 @@ from django import forms
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import Location, Rack, Region, Site, SiteGroup
|
from dcim.models import Location, Rack, Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelFilterForm
|
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import TenancyFilterForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple,
|
add_blank_choice, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple,
|
||||||
@ -39,13 +39,13 @@ IPADDRESS_MASK_LENGTH_CHOICES = add_blank_choice([
|
|||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
class VRFFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class VRFFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = VRF
|
model = VRF
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['import_target_id', 'export_target_id'],
|
('Route Targets', ('import_target_id', 'export_target_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
import_target_id = DynamicModelMultipleChoiceField(
|
import_target_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=RouteTarget.objects.all(),
|
queryset=RouteTarget.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -59,13 +59,13 @@ class VRFFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RouteTargetFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class RouteTargetFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = RouteTarget
|
model = RouteTarget
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['importing_vrf_id', 'exporting_vrf_id'],
|
('VRF', ('importing_vrf_id', 'exporting_vrf_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
importing_vrf_id = DynamicModelMultipleChoiceField(
|
importing_vrf_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -79,7 +79,7 @@ class RouteTargetFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RIRFilterForm(CustomFieldModelFilterForm):
|
class RIRFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = RIR
|
model = RIR
|
||||||
is_private = forms.NullBooleanField(
|
is_private = forms.NullBooleanField(
|
||||||
required=False,
|
required=False,
|
||||||
@ -91,13 +91,13 @@ class RIRFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class AggregateFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class AggregateFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Aggregate
|
model = Aggregate
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['family', 'rir_id'],
|
('Attributes', ('family', 'rir_id')),
|
||||||
['tenant_group_id', 'tenant_id']
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
family = forms.ChoiceField(
|
family = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=add_blank_choice(IPAddressFamilyChoices),
|
choices=add_blank_choice(IPAddressFamilyChoices),
|
||||||
@ -112,14 +112,13 @@ class AggregateFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ASNFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class ASNFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = ASN
|
model = ASN
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q'],
|
(None, ('q', 'tag')),
|
||||||
['rir_id'],
|
('Assignment', ('rir_id', 'site_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
['site_id'],
|
)
|
||||||
]
|
|
||||||
rir_id = DynamicModelMultipleChoiceField(
|
rir_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -130,22 +129,23 @@ class ASNFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
required=False,
|
required=False,
|
||||||
label=_('Site')
|
label=_('Site')
|
||||||
)
|
)
|
||||||
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class RoleFilterForm(CustomFieldModelFilterForm):
|
class RoleFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Role
|
model = Role
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class PrefixFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Prefix
|
model = Prefix
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['within_include', 'family', 'status', 'role_id', 'mask_length', 'is_pool', 'mark_utilized'],
|
('Addressing', ('within_include', 'family', 'status', 'role_id', 'mask_length', 'is_pool', 'mark_utilized')),
|
||||||
['vrf_id', 'present_in_vrf_id'],
|
('VRF', ('vrf_id', 'present_in_vrf_id')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['tenant_group_id', 'tenant_id']
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
mask_length__lte = forms.IntegerField(
|
mask_length__lte = forms.IntegerField(
|
||||||
widget=forms.HiddenInput()
|
widget=forms.HiddenInput()
|
||||||
)
|
)
|
||||||
@ -228,13 +228,13 @@ class PrefixFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class IPRangeFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = IPRange
|
model = IPRange
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['family', 'vrf_id', 'status', 'role_id'],
|
('Attriubtes', ('family', 'vrf_id', 'status', 'role_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
family = forms.ChoiceField(
|
family = forms.ChoiceField(
|
||||||
required=False,
|
required=False,
|
||||||
choices=add_blank_choice(IPAddressFamilyChoices),
|
choices=add_blank_choice(IPAddressFamilyChoices),
|
||||||
@ -261,14 +261,14 @@ class IPRangeFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class IPAddressFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class IPAddressFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = IPAddress
|
model = IPAddress
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface'],
|
('Attributes', ('parent', 'family', 'status', 'role', 'mask_length', 'assigned_to_interface')),
|
||||||
['vrf_id', 'present_in_vrf_id'],
|
('VRF', ('vrf_id', 'present_in_vrf_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
parent = forms.CharField(
|
parent = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
@ -321,12 +321,12 @@ class IPAddressFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class FHRPGroupFilterForm(CustomFieldModelFilterForm):
|
class FHRPGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = FHRPGroup
|
model = FHRPGroup
|
||||||
field_groups = (
|
fieldsets = (
|
||||||
('q', 'tag'),
|
(None, ('q', 'tag')),
|
||||||
('protocol', 'group_id'),
|
('Attributes', ('protocol', 'group_id')),
|
||||||
('auth_type', 'auth_key'),
|
('Authentication', ('auth_type', 'auth_key')),
|
||||||
)
|
)
|
||||||
protocol = forms.MultipleChoiceField(
|
protocol = forms.MultipleChoiceField(
|
||||||
choices=FHRPGroupProtocolChoices,
|
choices=FHRPGroupProtocolChoices,
|
||||||
@ -351,12 +351,12 @@ class FHRPGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupFilterForm(CustomFieldModelFilterForm):
|
class VLANGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region', 'sitegroup', 'site', 'location', 'rack'],
|
('Location', ('region', 'sitegroup', 'site', 'location', 'rack')),
|
||||||
['min_vid', 'max_vid'],
|
('VLAN ID', ('min_vid', 'max_vid')),
|
||||||
]
|
)
|
||||||
model = VLANGroup
|
model = VLANGroup
|
||||||
region = DynamicModelMultipleChoiceField(
|
region = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
@ -394,14 +394,14 @@ class VLANGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class VLANFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = VLAN
|
model = VLAN
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['group_id', 'status', 'role_id', 'vid'],
|
('Attributes', ('group_id', 'status', 'role_id', 'vid')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
region_id = DynamicModelMultipleChoiceField(
|
region_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Region.objects.all(),
|
queryset=Region.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -448,11 +448,11 @@ class VLANFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ServiceTemplateFilterForm(CustomFieldModelFilterForm):
|
class ServiceTemplateFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ServiceTemplate
|
model = ServiceTemplate
|
||||||
field_groups = (
|
fieldsets = (
|
||||||
('q', 'tag'),
|
(None, ('q', 'tag')),
|
||||||
('protocol', 'port'),
|
('Attributes', ('protocol', 'port')),
|
||||||
)
|
)
|
||||||
protocol = forms.ChoiceField(
|
protocol = forms.ChoiceField(
|
||||||
choices=add_blank_choice(ServiceProtocolChoices),
|
choices=add_blank_choice(ServiceProtocolChoices),
|
||||||
|
@ -2,13 +2,13 @@ from django import forms
|
|||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
from dcim.models import Device, Interface, Location, Rack, Region, Site, SiteGroup
|
from dcim.models import Device, Interface, Location, Rack, Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.choices import *
|
from ipam.choices import *
|
||||||
from ipam.constants import *
|
from ipam.constants import *
|
||||||
from ipam.formfields import IPNetworkFormField
|
from ipam.formfields import IPNetworkFormField
|
||||||
from ipam.models import *
|
from ipam.models import *
|
||||||
from ipam.models import ASN
|
from ipam.models import ASN
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.exceptions import PermissionsViolation
|
from utilities.exceptions import PermissionsViolation
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
@ -39,7 +39,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VRFForm(TenancyForm, CustomFieldModelForm):
|
class VRFForm(TenancyForm, NetBoxModelForm):
|
||||||
import_targets = DynamicModelMultipleChoiceField(
|
import_targets = DynamicModelMultipleChoiceField(
|
||||||
queryset=RouteTarget.objects.all(),
|
queryset=RouteTarget.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -53,17 +53,18 @@ class VRFForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('VRF', ('name', 'rd', 'enforce_unique', 'description', 'tags')),
|
||||||
|
('Route Targets', ('import_targets', 'export_targets')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VRF
|
model = VRF
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'rd', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tenant_group', 'tenant',
|
'name', 'rd', 'enforce_unique', 'description', 'import_targets', 'export_targets', 'tenant_group', 'tenant',
|
||||||
'tags',
|
'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('VRF', ('name', 'rd', 'enforce_unique', 'description', 'tags')),
|
|
||||||
('Route Targets', ('import_targets', 'export_targets')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
labels = {
|
labels = {
|
||||||
'rd': "RD",
|
'rd': "RD",
|
||||||
}
|
}
|
||||||
@ -72,24 +73,25 @@ class VRFForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RouteTargetForm(TenancyForm, CustomFieldModelForm):
|
class RouteTargetForm(TenancyForm, NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Route Target', ('name', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = RouteTarget
|
model = RouteTarget
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'description', 'tenant_group', 'tenant', 'tags',
|
'name', 'description', 'tenant_group', 'tenant', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Route Target', ('name', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class RIRForm(CustomFieldModelForm):
|
class RIRForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -103,7 +105,7 @@ class RIRForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AggregateForm(TenancyForm, CustomFieldModelForm):
|
class AggregateForm(TenancyForm, NetBoxModelForm):
|
||||||
rir = DynamicModelChoiceField(
|
rir = DynamicModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
label='RIR'
|
label='RIR'
|
||||||
@ -113,15 +115,16 @@ class AggregateForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Aggregate', ('prefix', 'rir', 'date_added', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Aggregate
|
model = Aggregate
|
||||||
fields = [
|
fields = [
|
||||||
'prefix', 'rir', 'date_added', 'description', 'tenant_group', 'tenant', 'tags',
|
'prefix', 'rir', 'date_added', 'description', 'tenant_group', 'tenant', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Aggregate', ('prefix', 'rir', 'date_added', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'prefix': "IPv4 or IPv6 network",
|
'prefix': "IPv4 or IPv6 network",
|
||||||
'rir': "Regional Internet Registry responsible for this prefix",
|
'rir': "Regional Internet Registry responsible for this prefix",
|
||||||
@ -131,7 +134,7 @@ class AggregateForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ASNForm(TenancyForm, CustomFieldModelForm):
|
class ASNForm(TenancyForm, NetBoxModelForm):
|
||||||
rir = DynamicModelChoiceField(
|
rir = DynamicModelChoiceField(
|
||||||
queryset=RIR.objects.all(),
|
queryset=RIR.objects.all(),
|
||||||
label='RIR',
|
label='RIR',
|
||||||
@ -146,15 +149,16 @@ class ASNForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('ASN', ('asn', 'rir', 'sites', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ASN
|
model = ASN
|
||||||
fields = [
|
fields = [
|
||||||
'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'tags'
|
'asn', 'rir', 'sites', 'tenant_group', 'tenant', 'description', 'tags'
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('ASN', ('asn', 'rir', 'sites', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'asn': "AS number",
|
'asn': "AS number",
|
||||||
'rir': "Regional Internet Registry responsible for this prefix",
|
'rir': "Regional Internet Registry responsible for this prefix",
|
||||||
@ -175,7 +179,7 @@ class ASNForm(TenancyForm, CustomFieldModelForm):
|
|||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class RoleForm(CustomFieldModelForm):
|
class RoleForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -189,7 +193,7 @@ class RoleForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PrefixForm(TenancyForm, CustomFieldModelForm):
|
class PrefixForm(TenancyForm, NetBoxModelForm):
|
||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -248,23 +252,24 @@ class PrefixForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Prefix', ('prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags')),
|
||||||
|
('Site/VLAN Assignment', ('region', 'site_group', 'site', 'vlan_group', 'vlan')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Prefix
|
model = Prefix
|
||||||
fields = [
|
fields = [
|
||||||
'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description',
|
'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'mark_utilized', 'description',
|
||||||
'tenant_group', 'tenant', 'tags',
|
'tenant_group', 'tenant', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Prefix', ('prefix', 'status', 'vrf', 'role', 'is_pool', 'mark_utilized', 'description', 'tags')),
|
|
||||||
('Site/VLAN Assignment', ('region', 'site_group', 'site', 'vlan_group', 'vlan')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect(),
|
'status': StaticSelect(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class IPRangeForm(TenancyForm, CustomFieldModelForm):
|
class IPRangeForm(TenancyForm, NetBoxModelForm):
|
||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -279,21 +284,22 @@ class IPRangeForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('IP Range', ('vrf', 'start_address', 'end_address', 'role', 'status', 'description', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IPRange
|
model = IPRange
|
||||||
fields = [
|
fields = [
|
||||||
'vrf', 'start_address', 'end_address', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags',
|
'vrf', 'start_address', 'end_address', 'status', 'role', 'description', 'tenant_group', 'tenant', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('IP Range', ('vrf', 'start_address', 'end_address', 'role', 'status', 'description', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect(),
|
'status': StaticSelect(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class IPAddressForm(TenancyForm, CustomFieldModelForm):
|
class IPAddressForm(TenancyForm, NetBoxModelForm):
|
||||||
device = DynamicModelChoiceField(
|
device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -506,7 +512,7 @@ class IPAddressForm(TenancyForm, CustomFieldModelForm):
|
|||||||
return ipaddress
|
return ipaddress
|
||||||
|
|
||||||
|
|
||||||
class IPAddressBulkAddForm(TenancyForm, CustomFieldModelForm):
|
class IPAddressBulkAddForm(TenancyForm, NetBoxModelForm):
|
||||||
vrf = DynamicModelChoiceField(
|
vrf = DynamicModelChoiceField(
|
||||||
queryset=VRF.objects.all(),
|
queryset=VRF.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -540,7 +546,7 @@ class IPAddressAssignForm(BootstrapMixin, forms.Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FHRPGroupForm(CustomFieldModelForm):
|
class FHRPGroupForm(NetBoxModelForm):
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -562,16 +568,17 @@ class FHRPGroupForm(CustomFieldModelForm):
|
|||||||
label='Status'
|
label='Status'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('FHRP Group', ('protocol', 'group_id', 'description', 'tags')),
|
||||||
|
('Authentication', ('auth_type', 'auth_key')),
|
||||||
|
('Virtual IP Address', ('ip_vrf', 'ip_address', 'ip_status'))
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FHRPGroup
|
model = FHRPGroup
|
||||||
fields = (
|
fields = (
|
||||||
'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_vrf', 'ip_address', 'ip_status', 'tags',
|
'protocol', 'group_id', 'auth_type', 'auth_key', 'description', 'ip_vrf', 'ip_address', 'ip_status', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('FHRP Group', ('protocol', 'group_id', 'description', 'tags')),
|
|
||||||
('Authentication', ('auth_type', 'auth_key')),
|
|
||||||
('Virtual IP Address', ('ip_vrf', 'ip_address', 'ip_status'))
|
|
||||||
)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
instance = super().save(*args, **kwargs)
|
instance = super().save(*args, **kwargs)
|
||||||
@ -629,7 +636,7 @@ class FHRPGroupAssignmentForm(BootstrapMixin, forms.ModelForm):
|
|||||||
self.fields['group'].widget.add_query_param('related_ip', ipaddress.pk)
|
self.fields['group'].widget.add_query_param('related_ip', ipaddress.pk)
|
||||||
|
|
||||||
|
|
||||||
class VLANGroupForm(CustomFieldModelForm):
|
class VLANGroupForm(NetBoxModelForm):
|
||||||
scope_type = ContentTypeChoiceField(
|
scope_type = ContentTypeChoiceField(
|
||||||
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
|
queryset=ContentType.objects.filter(model__in=VLANGROUP_SCOPE_TYPES),
|
||||||
required=False
|
required=False
|
||||||
@ -699,17 +706,18 @@ class VLANGroupForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('VLAN Group', ('name', 'slug', 'description', 'tags')),
|
||||||
|
('Child VLANs', ('min_vid', 'max_vid')),
|
||||||
|
('Scope', ('scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VLANGroup
|
model = VLANGroup
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'slug', 'description', 'scope_type', 'region', 'sitegroup', 'site', 'location', 'rack',
|
'name', 'slug', 'description', 'scope_type', 'region', 'sitegroup', 'site', 'location', 'rack',
|
||||||
'clustergroup', 'cluster', 'min_vid', 'max_vid', 'tags',
|
'clustergroup', 'cluster', 'min_vid', 'max_vid', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('VLAN Group', ('name', 'slug', 'description', 'tags')),
|
|
||||||
('Child VLANs', ('min_vid', 'max_vid')),
|
|
||||||
('Scope', ('scope_type', 'region', 'sitegroup', 'site', 'location', 'rack', 'clustergroup', 'cluster')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'scope_type': StaticSelect,
|
'scope_type': StaticSelect,
|
||||||
}
|
}
|
||||||
@ -736,7 +744,7 @@ class VLANGroupForm(CustomFieldModelForm):
|
|||||||
self.instance.scope_id = None
|
self.instance.scope_id = None
|
||||||
|
|
||||||
|
|
||||||
class VLANForm(TenancyForm, CustomFieldModelForm):
|
class VLANForm(TenancyForm, NetBoxModelForm):
|
||||||
# VLANGroup assignment fields
|
# VLANGroup assignment fields
|
||||||
scope_type = forms.ChoiceField(
|
scope_type = forms.ChoiceField(
|
||||||
choices=(
|
choices=(
|
||||||
@ -817,7 +825,7 @@ class VLANForm(TenancyForm, CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ServiceTemplateForm(CustomFieldModelForm):
|
class ServiceTemplateForm(NetBoxModelForm):
|
||||||
ports = NumericArrayField(
|
ports = NumericArrayField(
|
||||||
base_field=forms.IntegerField(
|
base_field=forms.IntegerField(
|
||||||
min_value=SERVICE_PORT_MIN,
|
min_value=SERVICE_PORT_MIN,
|
||||||
@ -838,7 +846,7 @@ class ServiceTemplateForm(CustomFieldModelForm):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ServiceForm(CustomFieldModelForm):
|
class ServiceForm(NetBoxModelForm):
|
||||||
device = DynamicModelChoiceField(
|
device = DynamicModelChoiceField(
|
||||||
queryset=Device.objects.all(),
|
queryset=Device.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from utilities.forms import BootstrapMixin
|
from utilities.forms import BootstrapMixin
|
||||||
|
from .base import *
|
||||||
|
|
||||||
OBJ_TYPE_CHOICES = (
|
OBJ_TYPE_CHOICES = (
|
||||||
('', 'All Objects'),
|
('', 'All Objects'),
|
126
netbox/netbox/forms/base.py
Normal file
126
netbox/netbox/forms/base.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
from django import forms
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from extras.choices import CustomFieldFilterLogicChoices, CustomFieldTypeChoices
|
||||||
|
from extras.forms.customfields import CustomFieldsMixin
|
||||||
|
from extras.models import CustomField, Tag
|
||||||
|
from utilities.forms import BootstrapMixin, BulkEditMixin, CSVModelForm
|
||||||
|
from utilities.forms.fields import DynamicModelMultipleChoiceField
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'NetBoxModelForm',
|
||||||
|
'NetBoxModelCSVForm',
|
||||||
|
'NetBoxModelBulkEditForm',
|
||||||
|
'NetBoxModelFilterSetForm',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxModelForm(BootstrapMixin, CustomFieldsMixin, forms.ModelForm):
|
||||||
|
"""
|
||||||
|
Base form for creating & editing NetBox models. Extends Django's ModelForm to add support for custom fields.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
|
||||||
|
the rendered form (optional). If not defined, the all fields will be rendered as a single section.
|
||||||
|
"""
|
||||||
|
fieldsets = ()
|
||||||
|
|
||||||
|
def _get_content_type(self):
|
||||||
|
return ContentType.objects.get_for_model(self._meta.model)
|
||||||
|
|
||||||
|
def _get_form_field(self, customfield):
|
||||||
|
if self.instance.pk:
|
||||||
|
form_field = customfield.to_form_field(set_initial=False)
|
||||||
|
form_field.initial = self.instance.custom_field_data.get(customfield.name, None)
|
||||||
|
return form_field
|
||||||
|
|
||||||
|
return customfield.to_form_field()
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
|
||||||
|
# Save custom field data on instance
|
||||||
|
for cf_name, customfield in self.custom_fields.items():
|
||||||
|
key = cf_name[3:] # Strip "cf_" from field name
|
||||||
|
value = self.cleaned_data.get(cf_name)
|
||||||
|
|
||||||
|
# Convert "empty" values to null
|
||||||
|
if value in self.fields[cf_name].empty_values:
|
||||||
|
self.instance.custom_field_data[key] = None
|
||||||
|
else:
|
||||||
|
self.instance.custom_field_data[key] = customfield.serialize(value)
|
||||||
|
|
||||||
|
return super().clean()
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxModelCSVForm(CSVModelForm, NetBoxModelForm):
|
||||||
|
"""
|
||||||
|
Base form for creating a NetBox objects from CSV data. Used for bulk importing.
|
||||||
|
"""
|
||||||
|
def _get_form_field(self, customfield):
|
||||||
|
return customfield.to_form_field(for_csv_import=True)
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxModelBulkEditForm(BootstrapMixin, CustomFieldsMixin, BulkEditMixin, forms.Form):
|
||||||
|
"""
|
||||||
|
Base form for modifying multiple NetBox objects (of the same type) in bulk via the UI. Adds support for custom
|
||||||
|
fields and adding/removing tags.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
nullable_fields: A list of field names indicating which fields support being set to null/empty
|
||||||
|
"""
|
||||||
|
add_tags = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Tag.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
remove_tags = DynamicModelMultipleChoiceField(
|
||||||
|
queryset=Tag.objects.all(),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_form_field(self, customfield):
|
||||||
|
return customfield.to_form_field(set_initial=False, enforce_required=False)
|
||||||
|
|
||||||
|
def _append_customfield_fields(self):
|
||||||
|
"""
|
||||||
|
Append form fields for all CustomFields assigned to this object type.
|
||||||
|
"""
|
||||||
|
nullable_custom_fields = []
|
||||||
|
for customfield in self._get_custom_fields(self._get_content_type()):
|
||||||
|
# Record non-required custom fields as nullable
|
||||||
|
if not customfield.required:
|
||||||
|
nullable_custom_fields.append(customfield.name)
|
||||||
|
|
||||||
|
self.fields[customfield.name] = self._get_form_field(customfield)
|
||||||
|
|
||||||
|
# Annotate the field in the list of CustomField form fields
|
||||||
|
self.custom_fields[customfield.name] = customfield
|
||||||
|
|
||||||
|
# Annotate nullable custom fields (if any) on the form instance
|
||||||
|
if nullable_custom_fields:
|
||||||
|
self.custom_fields = (*self.custom_fields, *nullable_custom_fields)
|
||||||
|
|
||||||
|
|
||||||
|
class NetBoxModelFilterSetForm(BootstrapMixin, CustomFieldsMixin, forms.Form):
|
||||||
|
"""
|
||||||
|
Base form for FilerSet forms. These are used to filter object lists in the NetBox UI. Note that the
|
||||||
|
corresponding FilterSet *must* provide a `q` filter.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
model: The model class associated with the form
|
||||||
|
fieldsets: An iterable of two-tuples which define a heading and field set to display per section of
|
||||||
|
the rendered form (optional). If not defined, the all fields will be rendered as a single section.
|
||||||
|
"""
|
||||||
|
q = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label='Search'
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_custom_fields(self, content_type):
|
||||||
|
return CustomField.objects.filter(content_types=content_type).exclude(
|
||||||
|
Q(filter_logic=CustomFieldFilterLogicChoices.FILTER_DISABLED) |
|
||||||
|
Q(type=CustomFieldTypeChoices.TYPE_JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_form_field(self, customfield):
|
||||||
|
return customfield.to_form_field(set_initial=False, enforce_required=False)
|
@ -675,14 +675,13 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
|
|||||||
"""
|
"""
|
||||||
Delete objects in bulk.
|
Delete objects in bulk.
|
||||||
|
|
||||||
filterset: FilterSet to apply when deleting by QuerySet
|
Attributes:
|
||||||
table: The table used to display devices being deleted
|
filterset: FilterSet to apply when deleting by QuerySet
|
||||||
form: The form class used to delete objects in bulk
|
table: The table used to display devices being deleted
|
||||||
"""
|
"""
|
||||||
template_name = 'generic/object_bulk_delete.html'
|
template_name = 'generic/object_bulk_delete.html'
|
||||||
filterset = None
|
filterset = None
|
||||||
table = None
|
table = None
|
||||||
form = None
|
|
||||||
|
|
||||||
def get_required_permission(self):
|
def get_required_permission(self):
|
||||||
return get_permission_for_model(self.queryset.model, 'delete')
|
return get_permission_for_model(self.queryset.model, 'delete')
|
||||||
@ -694,9 +693,6 @@ class BulkDeleteView(GetReturnURLMixin, BaseMultiObjectView):
|
|||||||
class BulkDeleteForm(ConfirmationForm):
|
class BulkDeleteForm(ConfirmationForm):
|
||||||
pk = ModelMultipleChoiceField(queryset=self.queryset, widget=MultipleHiddenInput)
|
pk = ModelMultipleChoiceField(queryset=self.queryset, widget=MultipleHiddenInput)
|
||||||
|
|
||||||
if self.form:
|
|
||||||
return self.form
|
|
||||||
|
|
||||||
return BulkDeleteForm
|
return BulkDeleteForm
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
{% block form %}
|
{% block form %}
|
||||||
{% if form.Meta.fieldsets %}
|
{% if form.fieldsets %}
|
||||||
|
|
||||||
{# Render hidden fields #}
|
{# Render hidden fields #}
|
||||||
{% for field in form.hidden_fields %}
|
{% for field in form.hidden_fields %}
|
||||||
@ -41,7 +41,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{# Render grouped fields according to Form #}
|
{# Render grouped fields according to Form #}
|
||||||
{% for group, fields in form.Meta.fieldsets %}
|
{% for group, fields in form.fieldsets %}
|
||||||
<div class="field-group mb-5">
|
<div class="field-group mb-5">
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<h5 class="offset-sm-3">{{ group }}</h5>
|
<h5 class="offset-sm-3">{{ group }}</h5>
|
||||||
|
@ -7,21 +7,22 @@
|
|||||||
{% for field in filter_form.hidden_fields %}
|
{% for field in filter_form.hidden_fields %}
|
||||||
{{ field }}
|
{{ field }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if filter_form.field_groups %}
|
{# List filters by group #}
|
||||||
{# List filters by group #}
|
{% for heading, fields in filter_form.fieldsets %}
|
||||||
{% for group in filter_form.field_groups %}
|
<div class="col col-12">
|
||||||
<div class="col col-12">
|
{% if heading %}
|
||||||
{% for name in group %}
|
<h6>{{ heading }}</h6>
|
||||||
{% with field=filter_form|get_item:name %}
|
|
||||||
{% render_field field %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% if not forloop.last %}
|
|
||||||
<hr class="card-divider mt-0" />
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% for name in fields %}
|
||||||
{% else %}
|
{% with field=filter_form|get_item:name %}
|
||||||
|
{% render_field field %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if not forloop.last %}
|
||||||
|
<hr class="card-divider mt-0" />
|
||||||
|
{% endif %}
|
||||||
|
{% empty %}
|
||||||
{# List all non-customfield filters as declared in the form class #}
|
{# List all non-customfield filters as declared in the form class #}
|
||||||
{% for field in filter_form.visible_fields %}
|
{% for field in filter_form.visible_fields %}
|
||||||
{% if not filter_form.custom_fields or field.name not in filter_form.custom_fields %}
|
{% if not filter_form.custom_fields or field.name not in filter_form.custom_fields %}
|
||||||
@ -30,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
{% if filter_form.custom_fields %}
|
{% if filter_form.custom_fields %}
|
||||||
{# List all custom field filters #}
|
{# List all custom field filters #}
|
||||||
<hr class="card-divider mt-0" />
|
<hr class="card-divider mt-0" />
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<form method="post" action="" id="preferences-update">
|
<form method="post" action="" id="preferences-update">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
{% for group, fields in form.Meta.fieldsets %}
|
{% for group, fields in form.fieldsets %}
|
||||||
<div class="field-group my-5">
|
<div class="field-group my-5">
|
||||||
<div class="row mb-2">
|
<div class="row mb-2">
|
||||||
<h5 class="offset-sm-3">{{ group }}</h5>
|
<h5 class="offset-sm-3">{{ group }}</h5>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
from utilities.forms import DynamicModelChoiceField
|
from utilities.forms import DynamicModelChoiceField
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ __all__ = (
|
|||||||
# Tenants
|
# Tenants
|
||||||
#
|
#
|
||||||
|
|
||||||
class TenantGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class TenantGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -31,11 +31,10 @@ class TenantGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'description')
|
||||||
nullable_fields = ['parent', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class TenantBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class TenantBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Tenant.objects.all(),
|
queryset=Tenant.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -45,17 +44,14 @@ class TenantBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('group',)
|
||||||
nullable_fields = [
|
|
||||||
'group',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Contacts
|
# Contacts
|
||||||
#
|
#
|
||||||
|
|
||||||
class ContactGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ContactGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -69,11 +65,10 @@ class ContactGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'description')
|
||||||
nullable_fields = ['parent', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class ContactRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ContactRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ContactRole.objects.all(),
|
queryset=ContactRole.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -83,11 +78,10 @@ class ContactRoleBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class ContactBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ContactBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Contact.objects.all(),
|
queryset=Contact.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -112,5 +106,4 @@ class ContactBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('group', 'title', 'phone', 'email', 'address', 'comments')
|
||||||
nullable_fields = ['group', 'title', 'phone', 'email', 'address', 'comments']
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from extras.forms import CustomFieldModelCSVForm
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
from utilities.forms import CSVModelChoiceField, SlugField
|
from utilities.forms import CSVModelChoiceField, SlugField
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ __all__ = (
|
|||||||
# Tenants
|
# Tenants
|
||||||
#
|
#
|
||||||
|
|
||||||
class TenantGroupCSVForm(CustomFieldModelCSVForm):
|
class TenantGroupCSVForm(NetBoxModelCSVForm):
|
||||||
parent = CSVModelChoiceField(
|
parent = CSVModelChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -29,7 +29,7 @@ class TenantGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'parent', 'description')
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class TenantCSVForm(CustomFieldModelCSVForm):
|
class TenantCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
group = CSVModelChoiceField(
|
group = CSVModelChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
@ -47,7 +47,7 @@ class TenantCSVForm(CustomFieldModelCSVForm):
|
|||||||
# Contacts
|
# Contacts
|
||||||
#
|
#
|
||||||
|
|
||||||
class ContactGroupCSVForm(CustomFieldModelCSVForm):
|
class ContactGroupCSVForm(NetBoxModelCSVForm):
|
||||||
parent = CSVModelChoiceField(
|
parent = CSVModelChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -61,7 +61,7 @@ class ContactGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'parent', 'description')
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ContactRoleCSVForm(CustomFieldModelCSVForm):
|
class ContactRoleCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -69,7 +69,7 @@ class ContactRoleCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'description')
|
fields = ('name', 'slug', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ContactCSVForm(CustomFieldModelCSVForm):
|
class ContactCSVForm(NetBoxModelCSVForm):
|
||||||
group = CSVModelChoiceField(
|
group = CSVModelChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from extras.forms import CustomFieldModelFilterForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField
|
from utilities.forms import DynamicModelMultipleChoiceField, TagFilterField
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ __all__ = (
|
|||||||
# Tenants
|
# Tenants
|
||||||
#
|
#
|
||||||
|
|
||||||
class TenantGroupFilterForm(CustomFieldModelFilterForm):
|
class TenantGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = TenantGroup
|
model = TenantGroup
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
@ -27,12 +27,8 @@ class TenantGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class TenantFilterForm(CustomFieldModelFilterForm):
|
class TenantFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Tenant
|
model = Tenant
|
||||||
field_groups = (
|
|
||||||
('q', 'tag'),
|
|
||||||
('group_id',),
|
|
||||||
)
|
|
||||||
group_id = DynamicModelMultipleChoiceField(
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -46,7 +42,7 @@ class TenantFilterForm(CustomFieldModelFilterForm):
|
|||||||
# Contacts
|
# Contacts
|
||||||
#
|
#
|
||||||
|
|
||||||
class ContactGroupFilterForm(CustomFieldModelFilterForm):
|
class ContactGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ContactGroup
|
model = ContactGroup
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
@ -56,17 +52,13 @@ class ContactGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ContactRoleFilterForm(CustomFieldModelFilterForm):
|
class ContactRoleFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ContactRole
|
model = ContactRole
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ContactFilterForm(CustomFieldModelFilterForm):
|
class ContactFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = Contact
|
model = Contact
|
||||||
field_groups = (
|
|
||||||
('q', 'tag'),
|
|
||||||
('group_id',),
|
|
||||||
)
|
|
||||||
group_id = DynamicModelMultipleChoiceField(
|
group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.models import *
|
from tenancy.models import *
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
BootstrapMixin, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, SmallTextarea,
|
BootstrapMixin, CommentField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, SmallTextarea,
|
||||||
@ -22,7 +22,7 @@ __all__ = (
|
|||||||
# Tenants
|
# Tenants
|
||||||
#
|
#
|
||||||
|
|
||||||
class TenantGroupForm(CustomFieldModelForm):
|
class TenantGroupForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -40,7 +40,7 @@ class TenantGroupForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TenantForm(CustomFieldModelForm):
|
class TenantForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
group = DynamicModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=TenantGroup.objects.all(),
|
queryset=TenantGroup.objects.all(),
|
||||||
@ -52,21 +52,22 @@ class TenantForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Tenant', ('name', 'slug', 'group', 'description', 'tags')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tenant
|
model = Tenant
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'slug', 'group', 'description', 'comments', 'tags',
|
'name', 'slug', 'group', 'description', 'comments', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('Tenant', ('name', 'slug', 'group', 'description', 'tags')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Contacts
|
# Contacts
|
||||||
#
|
#
|
||||||
|
|
||||||
class ContactGroupForm(CustomFieldModelForm):
|
class ContactGroupForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -82,7 +83,7 @@ class ContactGroupForm(CustomFieldModelForm):
|
|||||||
fields = ('parent', 'name', 'slug', 'description', 'tags')
|
fields = ('parent', 'name', 'slug', 'description', 'tags')
|
||||||
|
|
||||||
|
|
||||||
class ContactRoleForm(CustomFieldModelForm):
|
class ContactRoleForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -94,7 +95,7 @@ class ContactRoleForm(CustomFieldModelForm):
|
|||||||
fields = ('name', 'slug', 'description', 'tags')
|
fields = ('name', 'slug', 'description', 'tags')
|
||||||
|
|
||||||
|
|
||||||
class ContactForm(CustomFieldModelForm):
|
class ContactForm(NetBoxModelForm):
|
||||||
group = DynamicModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=ContactGroup.objects.all(),
|
queryset=ContactGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -105,14 +106,15 @@ class ContactForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Contact', ('group', 'name', 'title', 'phone', 'email', 'address', 'tags')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Contact
|
model = Contact
|
||||||
fields = (
|
fields = (
|
||||||
'group', 'name', 'title', 'phone', 'email', 'address', 'comments', 'tags',
|
'group', 'name', 'title', 'phone', 'email', 'address', 'comments', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('Contact', ('group', 'name', 'title', 'phone', 'email', 'address', 'tags')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'address': SmallTextarea(attrs={'rows': 3}),
|
'address': SmallTextarea(attrs={'rows': 3}),
|
||||||
}
|
}
|
||||||
|
@ -40,20 +40,20 @@ class UserConfigFormMetaclass(forms.models.ModelFormMetaclass):
|
|||||||
|
|
||||||
|
|
||||||
class UserConfigForm(BootstrapMixin, forms.ModelForm, metaclass=UserConfigFormMetaclass):
|
class UserConfigForm(BootstrapMixin, forms.ModelForm, metaclass=UserConfigFormMetaclass):
|
||||||
|
fieldsets = (
|
||||||
|
('User Interface', (
|
||||||
|
'pagination.per_page',
|
||||||
|
'pagination.placement',
|
||||||
|
'ui.colormode',
|
||||||
|
)),
|
||||||
|
('Miscellaneous', (
|
||||||
|
'data_format',
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserConfig
|
model = UserConfig
|
||||||
fields = ()
|
fields = ()
|
||||||
fieldsets = (
|
|
||||||
('User Interface', (
|
|
||||||
'pagination.per_page',
|
|
||||||
'pagination.placement',
|
|
||||||
'ui.colormode',
|
|
||||||
)),
|
|
||||||
('Miscellaneous', (
|
|
||||||
'data_format',
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, *args, instance=None, **kwargs):
|
def __init__(self, *args, instance=None, **kwargs):
|
||||||
|
|
||||||
|
@ -1,526 +0,0 @@
|
|||||||
import csv
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from io import StringIO
|
|
||||||
from netaddr import AddrFormatError, EUI
|
|
||||||
|
|
||||||
import django_filters
|
|
||||||
from django import forms
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
|
||||||
from django.db.models import Count, Q
|
|
||||||
from django.forms import BoundField
|
|
||||||
from django.forms.fields import JSONField as _JSONField, InvalidJSONInput
|
|
||||||
from django.urls import reverse
|
|
||||||
|
|
||||||
from utilities.choices import unpack_grouped_choices
|
|
||||||
from utilities.utils import content_type_identifier, content_type_name
|
|
||||||
from utilities.validators import EnhancedURLValidator
|
|
||||||
from . import widgets
|
|
||||||
from .constants import *
|
|
||||||
from .utils import expand_alphanumeric_pattern, expand_ipaddress_pattern, parse_csv, validate_csv
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
'ColorField',
|
|
||||||
'CommentField',
|
|
||||||
'ContentTypeChoiceField',
|
|
||||||
'ContentTypeMultipleChoiceField',
|
|
||||||
'CSVChoiceField',
|
|
||||||
'CSVContentTypeField',
|
|
||||||
'CSVDataField',
|
|
||||||
'CSVFileField',
|
|
||||||
'CSVModelChoiceField',
|
|
||||||
'CSVMultipleChoiceField',
|
|
||||||
'CSVMultipleContentTypeField',
|
|
||||||
'CSVTypedChoiceField',
|
|
||||||
'DynamicModelChoiceField',
|
|
||||||
'DynamicModelMultipleChoiceField',
|
|
||||||
'ExpandableIPAddressField',
|
|
||||||
'ExpandableNameField',
|
|
||||||
'JSONField',
|
|
||||||
'LaxURLField',
|
|
||||||
'MACAddressField',
|
|
||||||
'SlugField',
|
|
||||||
'TagFilterField',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CommentField(forms.CharField):
|
|
||||||
"""
|
|
||||||
A textarea with support for Markdown rendering. Exists mostly just to add a standard help_text.
|
|
||||||
"""
|
|
||||||
widget = forms.Textarea
|
|
||||||
default_label = ''
|
|
||||||
# TODO: Port Markdown cheat sheet to internal documentation
|
|
||||||
default_helptext = '<i class="mdi mdi-information-outline"></i> '\
|
|
||||||
'<a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet" target="_blank" tabindex="-1">'\
|
|
||||||
'Markdown</a> syntax is supported'
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
required = kwargs.pop('required', False)
|
|
||||||
label = kwargs.pop('label', self.default_label)
|
|
||||||
help_text = kwargs.pop('help_text', self.default_helptext)
|
|
||||||
super().__init__(required=required, label=label, help_text=help_text, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class SlugField(forms.SlugField):
|
|
||||||
"""
|
|
||||||
Extend the built-in SlugField to automatically populate from a field called `name` unless otherwise specified.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, slug_source='name', *args, **kwargs):
|
|
||||||
label = kwargs.pop('label', "Slug")
|
|
||||||
help_text = kwargs.pop('help_text', "URL-friendly unique shorthand")
|
|
||||||
widget = kwargs.pop('widget', widgets.SlugWidget)
|
|
||||||
super().__init__(label=label, help_text=help_text, widget=widget, *args, **kwargs)
|
|
||||||
self.widget.attrs['slug-source'] = slug_source
|
|
||||||
|
|
||||||
|
|
||||||
class ColorField(forms.CharField):
|
|
||||||
"""
|
|
||||||
A field which represents a color in hexadecimal RRGGBB format.
|
|
||||||
"""
|
|
||||||
widget = widgets.ColorSelect
|
|
||||||
|
|
||||||
|
|
||||||
class TagFilterField(forms.MultipleChoiceField):
|
|
||||||
"""
|
|
||||||
A filter field for the tags of a model. Only the tags used by a model are displayed.
|
|
||||||
|
|
||||||
:param model: The model of the filter
|
|
||||||
"""
|
|
||||||
widget = widgets.StaticSelectMultiple
|
|
||||||
|
|
||||||
def __init__(self, model, *args, **kwargs):
|
|
||||||
def get_choices():
|
|
||||||
tags = model.tags.annotate(
|
|
||||||
count=Count('extras_taggeditem_items')
|
|
||||||
).order_by('name')
|
|
||||||
return [
|
|
||||||
(str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags
|
|
||||||
]
|
|
||||||
|
|
||||||
# Choices are fetched each time the form is initialized
|
|
||||||
super().__init__(label='Tags', choices=get_choices, required=False, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class LaxURLField(forms.URLField):
|
|
||||||
"""
|
|
||||||
Modifies Django's built-in URLField to remove the requirement for fully-qualified domain names
|
|
||||||
(e.g. http://myserver/ is valid)
|
|
||||||
"""
|
|
||||||
default_validators = [EnhancedURLValidator()]
|
|
||||||
|
|
||||||
|
|
||||||
class JSONField(_JSONField):
|
|
||||||
"""
|
|
||||||
Custom wrapper around Django's built-in JSONField to avoid presenting "null" as the default text.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
if not self.help_text:
|
|
||||||
self.help_text = 'Enter context data in <a href="https://json.org/">JSON</a> format.'
|
|
||||||
self.widget.attrs['placeholder'] = ''
|
|
||||||
|
|
||||||
def prepare_value(self, value):
|
|
||||||
if isinstance(value, InvalidJSONInput):
|
|
||||||
return value
|
|
||||||
if value is None:
|
|
||||||
return ''
|
|
||||||
return json.dumps(value, sort_keys=True, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
class MACAddressField(forms.Field):
|
|
||||||
widget = forms.CharField
|
|
||||||
default_error_messages = {
|
|
||||||
'invalid': 'MAC address must be in EUI-48 format',
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
value = super().to_python(value)
|
|
||||||
|
|
||||||
# Validate MAC address format
|
|
||||||
try:
|
|
||||||
value = EUI(value.strip())
|
|
||||||
except AddrFormatError:
|
|
||||||
raise forms.ValidationError(self.error_messages['invalid'], code='invalid')
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Content type fields
|
|
||||||
#
|
|
||||||
|
|
||||||
class ContentTypeChoiceMixin:
|
|
||||||
|
|
||||||
def __init__(self, queryset, *args, **kwargs):
|
|
||||||
# Order ContentTypes by app_label
|
|
||||||
queryset = queryset.order_by('app_label', 'model')
|
|
||||||
super().__init__(queryset, *args, **kwargs)
|
|
||||||
|
|
||||||
def label_from_instance(self, obj):
|
|
||||||
try:
|
|
||||||
return content_type_name(obj)
|
|
||||||
except AttributeError:
|
|
||||||
return super().label_from_instance(obj)
|
|
||||||
|
|
||||||
|
|
||||||
class ContentTypeChoiceField(ContentTypeChoiceMixin, forms.ModelChoiceField):
|
|
||||||
widget = widgets.StaticSelect
|
|
||||||
|
|
||||||
|
|
||||||
class ContentTypeMultipleChoiceField(ContentTypeChoiceMixin, forms.ModelMultipleChoiceField):
|
|
||||||
widget = widgets.StaticSelectMultiple
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# CSV fields
|
|
||||||
#
|
|
||||||
|
|
||||||
class CSVDataField(forms.CharField):
|
|
||||||
"""
|
|
||||||
A CharField (rendered as a Textarea) which accepts CSV-formatted data. It returns data as a two-tuple: The first
|
|
||||||
item is a dictionary of column headers, mapping field names to the attribute by which they match a related object
|
|
||||||
(where applicable). The second item is a list of dictionaries, each representing a discrete row of CSV data.
|
|
||||||
|
|
||||||
:param from_form: The form from which the field derives its validation rules.
|
|
||||||
"""
|
|
||||||
widget = forms.Textarea
|
|
||||||
|
|
||||||
def __init__(self, from_form, *args, **kwargs):
|
|
||||||
|
|
||||||
form = from_form()
|
|
||||||
self.model = form.Meta.model
|
|
||||||
self.fields = form.fields
|
|
||||||
self.required_fields = [
|
|
||||||
name for name, field in form.fields.items() if field.required
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.strip = False
|
|
||||||
if not self.label:
|
|
||||||
self.label = ''
|
|
||||||
if not self.initial:
|
|
||||||
self.initial = ','.join(self.required_fields) + '\n'
|
|
||||||
if not self.help_text:
|
|
||||||
self.help_text = 'Enter the list of column headers followed by one line per record to be imported, using ' \
|
|
||||||
'commas to separate values. Multi-line data and values containing commas may be wrapped ' \
|
|
||||||
'in double quotes.'
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
reader = csv.reader(StringIO(value.strip()))
|
|
||||||
|
|
||||||
return parse_csv(reader)
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
headers, records = value
|
|
||||||
validate_csv(headers, self.fields, self.required_fields)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class CSVFileField(forms.FileField):
|
|
||||||
"""
|
|
||||||
A FileField (rendered as a file input button) which accepts a file containing CSV-formatted data. It returns
|
|
||||||
data as a two-tuple: The first item is a dictionary of column headers, mapping field names to the attribute
|
|
||||||
by which they match a related object (where applicable). The second item is a list of dictionaries, each
|
|
||||||
representing a discrete row of CSV data.
|
|
||||||
|
|
||||||
:param from_form: The form from which the field derives its validation rules.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, from_form, *args, **kwargs):
|
|
||||||
|
|
||||||
form = from_form()
|
|
||||||
self.model = form.Meta.model
|
|
||||||
self.fields = form.fields
|
|
||||||
self.required_fields = [
|
|
||||||
name for name, field in form.fields.items() if field.required
|
|
||||||
]
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def to_python(self, file):
|
|
||||||
if file is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
csv_str = file.read().decode('utf-8').strip()
|
|
||||||
reader = csv.reader(StringIO(csv_str))
|
|
||||||
headers, records = parse_csv(reader)
|
|
||||||
|
|
||||||
return headers, records
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
if value is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
headers, records = value
|
|
||||||
validate_csv(headers, self.fields, self.required_fields)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class CSVChoicesMixin:
|
|
||||||
STATIC_CHOICES = True
|
|
||||||
|
|
||||||
def __init__(self, *, choices=(), **kwargs):
|
|
||||||
super().__init__(choices=choices, **kwargs)
|
|
||||||
self.choices = unpack_grouped_choices(choices)
|
|
||||||
|
|
||||||
|
|
||||||
class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField):
|
|
||||||
"""
|
|
||||||
A CSV field which accepts a single selection value.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField):
|
|
||||||
"""
|
|
||||||
A CSV field which accepts multiple selection values.
|
|
||||||
"""
|
|
||||||
def to_python(self, value):
|
|
||||||
if not value:
|
|
||||||
return []
|
|
||||||
if not isinstance(value, str):
|
|
||||||
raise forms.ValidationError(f"Invalid value for a multiple choice field: {value}")
|
|
||||||
return value.split(',')
|
|
||||||
|
|
||||||
|
|
||||||
class CSVTypedChoiceField(forms.TypedChoiceField):
|
|
||||||
STATIC_CHOICES = True
|
|
||||||
|
|
||||||
|
|
||||||
class CSVModelChoiceField(forms.ModelChoiceField):
|
|
||||||
"""
|
|
||||||
Provides additional validation for model choices entered as CSV data.
|
|
||||||
"""
|
|
||||||
default_error_messages = {
|
|
||||||
'invalid_choice': 'Object not found.',
|
|
||||||
}
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
try:
|
|
||||||
return super().to_python(value)
|
|
||||||
except MultipleObjectsReturned:
|
|
||||||
raise forms.ValidationError(
|
|
||||||
f'"{value}" is not a unique value for this field; multiple objects were found'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CSVContentTypeField(CSVModelChoiceField):
|
|
||||||
"""
|
|
||||||
Reference a ContentType in the form <app>.<model>
|
|
||||||
"""
|
|
||||||
STATIC_CHOICES = True
|
|
||||||
|
|
||||||
def prepare_value(self, value):
|
|
||||||
return content_type_identifier(value)
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
app_label, model = value.split('.')
|
|
||||||
except ValueError:
|
|
||||||
raise forms.ValidationError(f'Object type must be specified as "<app>.<model>"')
|
|
||||||
try:
|
|
||||||
return self.queryset.get(app_label=app_label, model=model)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
raise forms.ValidationError(f'Invalid object type')
|
|
||||||
|
|
||||||
|
|
||||||
class CSVMultipleContentTypeField(forms.ModelMultipleChoiceField):
|
|
||||||
STATIC_CHOICES = True
|
|
||||||
|
|
||||||
# TODO: Improve validation of selected ContentTypes
|
|
||||||
def prepare_value(self, value):
|
|
||||||
if type(value) is str:
|
|
||||||
ct_filter = Q()
|
|
||||||
for name in value.split(','):
|
|
||||||
app_label, model = name.split('.')
|
|
||||||
ct_filter |= Q(app_label=app_label, model=model)
|
|
||||||
return list(ContentType.objects.filter(ct_filter).values_list('pk', flat=True))
|
|
||||||
return content_type_identifier(value)
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Expansion fields
|
|
||||||
#
|
|
||||||
|
|
||||||
class ExpandableNameField(forms.CharField):
|
|
||||||
"""
|
|
||||||
A field which allows for numeric range expansion
|
|
||||||
Example: 'Gi0/[1-3]' => ['Gi0/1', 'Gi0/2', 'Gi0/3']
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
if not self.help_text:
|
|
||||||
self.help_text = """
|
|
||||||
Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range
|
|
||||||
are not supported. Example: <code>[ge,xe]-0/0/[0-9]</code>
|
|
||||||
"""
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
if not value:
|
|
||||||
return ''
|
|
||||||
if re.search(ALPHANUMERIC_EXPANSION_PATTERN, value):
|
|
||||||
return list(expand_alphanumeric_pattern(value))
|
|
||||||
return [value]
|
|
||||||
|
|
||||||
|
|
||||||
class ExpandableIPAddressField(forms.CharField):
|
|
||||||
"""
|
|
||||||
A field which allows for expansion of IP address ranges
|
|
||||||
Example: '192.0.2.[1-254]/24' => ['192.0.2.1/24', '192.0.2.2/24', '192.0.2.3/24' ... '192.0.2.254/24']
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
if not self.help_text:
|
|
||||||
self.help_text = 'Specify a numeric range to create multiple IPs.<br />'\
|
|
||||||
'Example: <code>192.0.2.[1,5,100-254]/24</code>'
|
|
||||||
|
|
||||||
def to_python(self, value):
|
|
||||||
# Hackish address family detection but it's all we have to work with
|
|
||||||
if '.' in value and re.search(IP4_EXPANSION_PATTERN, value):
|
|
||||||
return list(expand_ipaddress_pattern(value, 4))
|
|
||||||
elif ':' in value and re.search(IP6_EXPANSION_PATTERN, value):
|
|
||||||
return list(expand_ipaddress_pattern(value, 6))
|
|
||||||
return [value]
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Dynamic fields
|
|
||||||
#
|
|
||||||
|
|
||||||
class DynamicModelChoiceMixin:
|
|
||||||
"""
|
|
||||||
:param query_params: A dictionary of additional key/value pairs to attach to the API request
|
|
||||||
:param initial_params: A dictionary of child field references to use for selecting a parent field's initial value
|
|
||||||
:param null_option: The string used to represent a null selection (if any)
|
|
||||||
:param disabled_indicator: The name of the field which, if populated, will disable selection of the
|
|
||||||
choice (optional)
|
|
||||||
:param str fetch_trigger: The event type which will cause the select element to
|
|
||||||
fetch data from the API. Must be 'load', 'open', or 'collapse'. (optional)
|
|
||||||
"""
|
|
||||||
filter = django_filters.ModelChoiceFilter
|
|
||||||
widget = widgets.APISelect
|
|
||||||
|
|
||||||
def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None,
|
|
||||||
fetch_trigger=None, empty_label=None, *args, **kwargs):
|
|
||||||
self.query_params = query_params or {}
|
|
||||||
self.initial_params = initial_params or {}
|
|
||||||
self.null_option = null_option
|
|
||||||
self.disabled_indicator = disabled_indicator
|
|
||||||
self.fetch_trigger = fetch_trigger
|
|
||||||
|
|
||||||
# to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
|
|
||||||
# by widget_attrs()
|
|
||||||
self.to_field_name = kwargs.get('to_field_name')
|
|
||||||
self.empty_option = empty_label or ""
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def widget_attrs(self, widget):
|
|
||||||
attrs = {
|
|
||||||
'data-empty-option': self.empty_option
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set value-field attribute if the field specifies to_field_name
|
|
||||||
if self.to_field_name:
|
|
||||||
attrs['value-field'] = self.to_field_name
|
|
||||||
|
|
||||||
# Set the string used to represent a null option
|
|
||||||
if self.null_option is not None:
|
|
||||||
attrs['data-null-option'] = self.null_option
|
|
||||||
|
|
||||||
# Set the disabled indicator, if any
|
|
||||||
if self.disabled_indicator is not None:
|
|
||||||
attrs['disabled-indicator'] = self.disabled_indicator
|
|
||||||
|
|
||||||
# Set the fetch trigger, if any.
|
|
||||||
if self.fetch_trigger is not None:
|
|
||||||
attrs['data-fetch-trigger'] = self.fetch_trigger
|
|
||||||
|
|
||||||
# Attach any static query parameters
|
|
||||||
if (len(self.query_params) > 0):
|
|
||||||
widget.add_query_params(self.query_params)
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
|
|
||||||
def get_bound_field(self, form, field_name):
|
|
||||||
bound_field = BoundField(form, self, field_name)
|
|
||||||
|
|
||||||
# Set initial value based on prescribed child fields (if not already set)
|
|
||||||
if not self.initial and self.initial_params:
|
|
||||||
filter_kwargs = {}
|
|
||||||
for kwarg, child_field in self.initial_params.items():
|
|
||||||
value = form.initial.get(child_field.lstrip('$'))
|
|
||||||
if value:
|
|
||||||
filter_kwargs[kwarg] = value
|
|
||||||
if filter_kwargs:
|
|
||||||
self.initial = self.queryset.filter(**filter_kwargs).first()
|
|
||||||
|
|
||||||
# Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options
|
|
||||||
# will be populated on-demand via the APISelect widget.
|
|
||||||
data = bound_field.value()
|
|
||||||
if data:
|
|
||||||
field_name = getattr(self, 'to_field_name') or 'pk'
|
|
||||||
filter = self.filter(field_name=field_name)
|
|
||||||
try:
|
|
||||||
self.queryset = filter.filter(self.queryset, data)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
# Catch any error caused by invalid initial data passed from the user
|
|
||||||
self.queryset = self.queryset.none()
|
|
||||||
else:
|
|
||||||
self.queryset = self.queryset.none()
|
|
||||||
|
|
||||||
# Set the data URL on the APISelect widget (if not already set)
|
|
||||||
widget = bound_field.field.widget
|
|
||||||
if not widget.attrs.get('data-url'):
|
|
||||||
app_label = self.queryset.model._meta.app_label
|
|
||||||
model_name = self.queryset.model._meta.model_name
|
|
||||||
data_url = reverse('{}-api:{}-list'.format(app_label, model_name))
|
|
||||||
widget.attrs['data-url'] = data_url
|
|
||||||
|
|
||||||
return bound_field
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicModelChoiceField(DynamicModelChoiceMixin, forms.ModelChoiceField):
|
|
||||||
"""
|
|
||||||
Override get_bound_field() to avoid pre-populating field choices with a SQL query. The field will be
|
|
||||||
rendered only with choices set via bound data. Choices are populated on-demand via the APISelect widget.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def clean(self, value):
|
|
||||||
"""
|
|
||||||
When null option is enabled and "None" is sent as part of a form to be submitted, it is sent as the
|
|
||||||
string 'null'. This will check for that condition and gracefully handle the conversion to a NoneType.
|
|
||||||
"""
|
|
||||||
if self.null_option is not None and value == settings.FILTERS_NULL_CHOICE_VALUE:
|
|
||||||
return None
|
|
||||||
return super().clean(value)
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultipleChoiceField):
|
|
||||||
"""
|
|
||||||
A multiple-choice version of DynamicModelChoiceField.
|
|
||||||
"""
|
|
||||||
filter = django_filters.ModelMultipleChoiceFilter
|
|
||||||
widget = widgets.APISelectMultiple
|
|
||||||
|
|
||||||
def clean(self, value):
|
|
||||||
"""
|
|
||||||
When null option is enabled and "None" is sent as part of a form to be submitted, it is sent as the
|
|
||||||
string 'null'. This will check for that condition and gracefully handle the conversion to a NoneType.
|
|
||||||
"""
|
|
||||||
if self.null_option is not None and settings.FILTERS_NULL_CHOICE_VALUE in value:
|
|
||||||
value = [v for v in value if v != settings.FILTERS_NULL_CHOICE_VALUE]
|
|
||||||
return [None, *value]
|
|
||||||
return super().clean(value)
|
|
5
netbox/utilities/forms/fields/__init__.py
Normal file
5
netbox/utilities/forms/fields/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from .content_types import *
|
||||||
|
from .csv import *
|
||||||
|
from .dynamic import *
|
||||||
|
from .expandable import *
|
||||||
|
from .fields import *
|
37
netbox/utilities/forms/fields/content_types.py
Normal file
37
netbox/utilities/forms/fields/content_types.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from utilities.forms import widgets
|
||||||
|
from utilities.utils import content_type_name
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ContentTypeChoiceField',
|
||||||
|
'ContentTypeMultipleChoiceField',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeChoiceMixin:
|
||||||
|
|
||||||
|
def __init__(self, queryset, *args, **kwargs):
|
||||||
|
# Order ContentTypes by app_label
|
||||||
|
queryset = queryset.order_by('app_label', 'model')
|
||||||
|
super().__init__(queryset, *args, **kwargs)
|
||||||
|
|
||||||
|
def label_from_instance(self, obj):
|
||||||
|
try:
|
||||||
|
return content_type_name(obj)
|
||||||
|
except AttributeError:
|
||||||
|
return super().label_from_instance(obj)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeChoiceField(ContentTypeChoiceMixin, forms.ModelChoiceField):
|
||||||
|
"""
|
||||||
|
Selection field for a single content type.
|
||||||
|
"""
|
||||||
|
widget = widgets.StaticSelect
|
||||||
|
|
||||||
|
|
||||||
|
class ContentTypeMultipleChoiceField(ContentTypeChoiceMixin, forms.ModelMultipleChoiceField):
|
||||||
|
"""
|
||||||
|
Selection field for one or more content types.
|
||||||
|
"""
|
||||||
|
widget = widgets.StaticSelectMultiple
|
193
netbox/utilities/forms/fields/csv.py
Normal file
193
netbox/utilities/forms/fields/csv.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import csv
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from utilities.choices import unpack_grouped_choices
|
||||||
|
from utilities.forms.utils import parse_csv, validate_csv
|
||||||
|
from utilities.utils import content_type_identifier
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'CSVChoiceField',
|
||||||
|
'CSVContentTypeField',
|
||||||
|
'CSVDataField',
|
||||||
|
'CSVFileField',
|
||||||
|
'CSVModelChoiceField',
|
||||||
|
'CSVMultipleChoiceField',
|
||||||
|
'CSVMultipleContentTypeField',
|
||||||
|
'CSVTypedChoiceField',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CSVDataField(forms.CharField):
|
||||||
|
"""
|
||||||
|
A CharField (rendered as a Textarea) which accepts CSV-formatted data. It returns data as a two-tuple: The first
|
||||||
|
item is a dictionary of column headers, mapping field names to the attribute by which they match a related object
|
||||||
|
(where applicable). The second item is a list of dictionaries, each representing a discrete row of CSV data.
|
||||||
|
|
||||||
|
:param from_form: The form from which the field derives its validation rules.
|
||||||
|
"""
|
||||||
|
widget = forms.Textarea
|
||||||
|
|
||||||
|
def __init__(self, from_form, *args, **kwargs):
|
||||||
|
|
||||||
|
form = from_form()
|
||||||
|
self.model = form.Meta.model
|
||||||
|
self.fields = form.fields
|
||||||
|
self.required_fields = [
|
||||||
|
name for name, field in form.fields.items() if field.required
|
||||||
|
]
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.strip = False
|
||||||
|
if not self.label:
|
||||||
|
self.label = ''
|
||||||
|
if not self.initial:
|
||||||
|
self.initial = ','.join(self.required_fields) + '\n'
|
||||||
|
if not self.help_text:
|
||||||
|
self.help_text = 'Enter the list of column headers followed by one line per record to be imported, using ' \
|
||||||
|
'commas to separate values. Multi-line data and values containing commas may be wrapped ' \
|
||||||
|
'in double quotes.'
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
reader = csv.reader(StringIO(value.strip()))
|
||||||
|
|
||||||
|
return parse_csv(reader)
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
headers, records = value
|
||||||
|
validate_csv(headers, self.fields, self.required_fields)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class CSVFileField(forms.FileField):
|
||||||
|
"""
|
||||||
|
A FileField (rendered as a file input button) which accepts a file containing CSV-formatted data. It returns
|
||||||
|
data as a two-tuple: The first item is a dictionary of column headers, mapping field names to the attribute
|
||||||
|
by which they match a related object (where applicable). The second item is a list of dictionaries, each
|
||||||
|
representing a discrete row of CSV data.
|
||||||
|
|
||||||
|
:param from_form: The form from which the field derives its validation rules.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, from_form, *args, **kwargs):
|
||||||
|
|
||||||
|
form = from_form()
|
||||||
|
self.model = form.Meta.model
|
||||||
|
self.fields = form.fields
|
||||||
|
self.required_fields = [
|
||||||
|
name for name, field in form.fields.items() if field.required
|
||||||
|
]
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def to_python(self, file):
|
||||||
|
if file is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
csv_str = file.read().decode('utf-8').strip()
|
||||||
|
reader = csv.reader(StringIO(csv_str))
|
||||||
|
headers, records = parse_csv(reader)
|
||||||
|
|
||||||
|
return headers, records
|
||||||
|
|
||||||
|
def validate(self, value):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
headers, records = value
|
||||||
|
validate_csv(headers, self.fields, self.required_fields)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class CSVChoicesMixin:
|
||||||
|
STATIC_CHOICES = True
|
||||||
|
|
||||||
|
def __init__(self, *, choices=(), **kwargs):
|
||||||
|
super().__init__(choices=choices, **kwargs)
|
||||||
|
self.choices = unpack_grouped_choices(choices)
|
||||||
|
|
||||||
|
|
||||||
|
class CSVChoiceField(CSVChoicesMixin, forms.ChoiceField):
|
||||||
|
"""
|
||||||
|
A CSV field which accepts a single selection value.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CSVMultipleChoiceField(CSVChoicesMixin, forms.MultipleChoiceField):
|
||||||
|
"""
|
||||||
|
A CSV field which accepts multiple selection values.
|
||||||
|
"""
|
||||||
|
def to_python(self, value):
|
||||||
|
if not value:
|
||||||
|
return []
|
||||||
|
if not isinstance(value, str):
|
||||||
|
raise forms.ValidationError(f"Invalid value for a multiple choice field: {value}")
|
||||||
|
return value.split(',')
|
||||||
|
|
||||||
|
|
||||||
|
class CSVTypedChoiceField(forms.TypedChoiceField):
|
||||||
|
STATIC_CHOICES = True
|
||||||
|
|
||||||
|
|
||||||
|
class CSVModelChoiceField(forms.ModelChoiceField):
|
||||||
|
"""
|
||||||
|
Extends Django's `ModelChoiceField` to provide additional validation for CSV values.
|
||||||
|
"""
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid_choice': 'Object not found.',
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
try:
|
||||||
|
return super().to_python(value)
|
||||||
|
except MultipleObjectsReturned:
|
||||||
|
raise forms.ValidationError(
|
||||||
|
f'"{value}" is not a unique value for this field; multiple objects were found'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CSVContentTypeField(CSVModelChoiceField):
|
||||||
|
"""
|
||||||
|
CSV field for referencing a single content type, in the form `<app>.<model>`.
|
||||||
|
"""
|
||||||
|
STATIC_CHOICES = True
|
||||||
|
|
||||||
|
def prepare_value(self, value):
|
||||||
|
return content_type_identifier(value)
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
app_label, model = value.split('.')
|
||||||
|
except ValueError:
|
||||||
|
raise forms.ValidationError(f'Object type must be specified as "<app>.<model>"')
|
||||||
|
try:
|
||||||
|
return self.queryset.get(app_label=app_label, model=model)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
raise forms.ValidationError(f'Invalid object type')
|
||||||
|
|
||||||
|
|
||||||
|
class CSVMultipleContentTypeField(forms.ModelMultipleChoiceField):
|
||||||
|
"""
|
||||||
|
CSV field for referencing one or more content types, in the form `<app>.<model>`.
|
||||||
|
"""
|
||||||
|
STATIC_CHOICES = True
|
||||||
|
|
||||||
|
# TODO: Improve validation of selected ContentTypes
|
||||||
|
def prepare_value(self, value):
|
||||||
|
if type(value) is str:
|
||||||
|
ct_filter = Q()
|
||||||
|
for name in value.split(','):
|
||||||
|
app_label, model = name.split('.')
|
||||||
|
ct_filter |= Q(app_label=app_label, model=model)
|
||||||
|
return list(ContentType.objects.filter(ct_filter).values_list('pk', flat=True))
|
||||||
|
return content_type_identifier(value)
|
141
netbox/utilities/forms/fields/dynamic.py
Normal file
141
netbox/utilities/forms/fields/dynamic.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import django_filters
|
||||||
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.forms import BoundField
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from utilities.forms import widgets
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'DynamicModelChoiceField',
|
||||||
|
'DynamicModelMultipleChoiceField',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicModelChoiceMixin:
|
||||||
|
"""
|
||||||
|
Override `get_bound_field()` to avoid pre-populating field choices with a SQL query. The field will be
|
||||||
|
rendered only with choices set via bound data. Choices are populated on-demand via the APISelect widget.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
query_params: A dictionary of additional key/value pairs to attach to the API request
|
||||||
|
initial_params: A dictionary of child field references to use for selecting a parent field's initial value
|
||||||
|
null_option: The string used to represent a null selection (if any)
|
||||||
|
disabled_indicator: The name of the field which, if populated, will disable selection of the
|
||||||
|
choice (optional)
|
||||||
|
fetch_trigger: The event type which will cause the select element to
|
||||||
|
fetch data from the API. Must be 'load', 'open', or 'collapse'. (optional)
|
||||||
|
"""
|
||||||
|
filter = django_filters.ModelChoiceFilter
|
||||||
|
widget = widgets.APISelect
|
||||||
|
|
||||||
|
def __init__(self, query_params=None, initial_params=None, null_option=None, disabled_indicator=None,
|
||||||
|
fetch_trigger=None, empty_label=None, *args, **kwargs):
|
||||||
|
self.query_params = query_params or {}
|
||||||
|
self.initial_params = initial_params or {}
|
||||||
|
self.null_option = null_option
|
||||||
|
self.disabled_indicator = disabled_indicator
|
||||||
|
self.fetch_trigger = fetch_trigger
|
||||||
|
|
||||||
|
# to_field_name is set by ModelChoiceField.__init__(), but we need to set it early for reference
|
||||||
|
# by widget_attrs()
|
||||||
|
self.to_field_name = kwargs.get('to_field_name')
|
||||||
|
self.empty_option = empty_label or ""
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def widget_attrs(self, widget):
|
||||||
|
attrs = {
|
||||||
|
'data-empty-option': self.empty_option
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set value-field attribute if the field specifies to_field_name
|
||||||
|
if self.to_field_name:
|
||||||
|
attrs['value-field'] = self.to_field_name
|
||||||
|
|
||||||
|
# Set the string used to represent a null option
|
||||||
|
if self.null_option is not None:
|
||||||
|
attrs['data-null-option'] = self.null_option
|
||||||
|
|
||||||
|
# Set the disabled indicator, if any
|
||||||
|
if self.disabled_indicator is not None:
|
||||||
|
attrs['disabled-indicator'] = self.disabled_indicator
|
||||||
|
|
||||||
|
# Set the fetch trigger, if any.
|
||||||
|
if self.fetch_trigger is not None:
|
||||||
|
attrs['data-fetch-trigger'] = self.fetch_trigger
|
||||||
|
|
||||||
|
# Attach any static query parameters
|
||||||
|
if (len(self.query_params) > 0):
|
||||||
|
widget.add_query_params(self.query_params)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def get_bound_field(self, form, field_name):
|
||||||
|
bound_field = BoundField(form, self, field_name)
|
||||||
|
|
||||||
|
# Set initial value based on prescribed child fields (if not already set)
|
||||||
|
if not self.initial and self.initial_params:
|
||||||
|
filter_kwargs = {}
|
||||||
|
for kwarg, child_field in self.initial_params.items():
|
||||||
|
value = form.initial.get(child_field.lstrip('$'))
|
||||||
|
if value:
|
||||||
|
filter_kwargs[kwarg] = value
|
||||||
|
if filter_kwargs:
|
||||||
|
self.initial = self.queryset.filter(**filter_kwargs).first()
|
||||||
|
|
||||||
|
# Modify the QuerySet of the field before we return it. Limit choices to any data already bound: Options
|
||||||
|
# will be populated on-demand via the APISelect widget.
|
||||||
|
data = bound_field.value()
|
||||||
|
if data:
|
||||||
|
field_name = getattr(self, 'to_field_name') or 'pk'
|
||||||
|
filter = self.filter(field_name=field_name)
|
||||||
|
try:
|
||||||
|
self.queryset = filter.filter(self.queryset, data)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
# Catch any error caused by invalid initial data passed from the user
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
else:
|
||||||
|
self.queryset = self.queryset.none()
|
||||||
|
|
||||||
|
# Set the data URL on the APISelect widget (if not already set)
|
||||||
|
widget = bound_field.field.widget
|
||||||
|
if not widget.attrs.get('data-url'):
|
||||||
|
app_label = self.queryset.model._meta.app_label
|
||||||
|
model_name = self.queryset.model._meta.model_name
|
||||||
|
data_url = reverse('{}-api:{}-list'.format(app_label, model_name))
|
||||||
|
widget.attrs['data-url'] = data_url
|
||||||
|
|
||||||
|
return bound_field
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicModelChoiceField(DynamicModelChoiceMixin, forms.ModelChoiceField):
|
||||||
|
"""
|
||||||
|
Dynamic selection field for a single object, backed by NetBox's REST API.
|
||||||
|
"""
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
When null option is enabled and "None" is sent as part of a form to be submitted, it is sent as the
|
||||||
|
string 'null'. This will check for that condition and gracefully handle the conversion to a NoneType.
|
||||||
|
"""
|
||||||
|
if self.null_option is not None and value == settings.FILTERS_NULL_CHOICE_VALUE:
|
||||||
|
return None
|
||||||
|
return super().clean(value)
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicModelMultipleChoiceField(DynamicModelChoiceMixin, forms.ModelMultipleChoiceField):
|
||||||
|
"""
|
||||||
|
A multiple-choice version of `DynamicModelChoiceField`.
|
||||||
|
"""
|
||||||
|
filter = django_filters.ModelMultipleChoiceFilter
|
||||||
|
widget = widgets.APISelectMultiple
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
"""
|
||||||
|
When null option is enabled and "None" is sent as part of a form to be submitted, it is sent as the
|
||||||
|
string 'null'. This will check for that condition and gracefully handle the conversion to a NoneType.
|
||||||
|
"""
|
||||||
|
if self.null_option is not None and settings.FILTERS_NULL_CHOICE_VALUE in value:
|
||||||
|
value = [v for v in value if v != settings.FILTERS_NULL_CHOICE_VALUE]
|
||||||
|
return [None, *value]
|
||||||
|
return super().clean(value)
|
54
netbox/utilities/forms/fields/expandable.py
Normal file
54
netbox/utilities/forms/fields/expandable.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from utilities.forms.constants import *
|
||||||
|
from utilities.forms.utils import expand_alphanumeric_pattern, expand_ipaddress_pattern
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ExpandableIPAddressField',
|
||||||
|
'ExpandableNameField',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpandableNameField(forms.CharField):
|
||||||
|
"""
|
||||||
|
A field which allows for numeric range expansion
|
||||||
|
Example: 'Gi0/[1-3]' => ['Gi0/1', 'Gi0/2', 'Gi0/3']
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if not self.help_text:
|
||||||
|
self.help_text = """
|
||||||
|
Alphanumeric ranges are supported for bulk creation. Mixed cases and types within a single range
|
||||||
|
are not supported. Example: <code>[ge,xe]-0/0/[0-9]</code>
|
||||||
|
"""
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
if not value:
|
||||||
|
return ''
|
||||||
|
if re.search(ALPHANUMERIC_EXPANSION_PATTERN, value):
|
||||||
|
return list(expand_alphanumeric_pattern(value))
|
||||||
|
return [value]
|
||||||
|
|
||||||
|
|
||||||
|
class ExpandableIPAddressField(forms.CharField):
|
||||||
|
"""
|
||||||
|
A field which allows for expansion of IP address ranges
|
||||||
|
Example: '192.0.2.[1-254]/24' => ['192.0.2.1/24', '192.0.2.2/24', '192.0.2.3/24' ... '192.0.2.254/24']
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if not self.help_text:
|
||||||
|
self.help_text = 'Specify a numeric range to create multiple IPs.<br />'\
|
||||||
|
'Example: <code>192.0.2.[1,5,100-254]/24</code>'
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
# Hackish address family detection but it's all we have to work with
|
||||||
|
if '.' in value and re.search(IP4_EXPANSION_PATTERN, value):
|
||||||
|
return list(expand_ipaddress_pattern(value, 4))
|
||||||
|
elif ':' in value and re.search(IP6_EXPANSION_PATTERN, value):
|
||||||
|
return list(expand_ipaddress_pattern(value, 6))
|
||||||
|
return [value]
|
127
netbox/utilities/forms/fields/fields.py
Normal file
127
netbox/utilities/forms/fields/fields.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.db.models import Count
|
||||||
|
from django.forms.fields import JSONField as _JSONField, InvalidJSONInput
|
||||||
|
from netaddr import AddrFormatError, EUI
|
||||||
|
|
||||||
|
from utilities.forms import widgets
|
||||||
|
from utilities.validators import EnhancedURLValidator
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'ColorField',
|
||||||
|
'CommentField',
|
||||||
|
'JSONField',
|
||||||
|
'LaxURLField',
|
||||||
|
'MACAddressField',
|
||||||
|
'SlugField',
|
||||||
|
'TagFilterField',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CommentField(forms.CharField):
|
||||||
|
"""
|
||||||
|
A textarea with support for Markdown rendering. Exists mostly just to add a standard `help_text`.
|
||||||
|
"""
|
||||||
|
widget = forms.Textarea
|
||||||
|
# TODO: Port Markdown cheat sheet to internal documentation
|
||||||
|
help_text = """
|
||||||
|
<i class="mdi mdi-information-outline"></i>
|
||||||
|
<a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet" target="_blank" tabindex="-1">
|
||||||
|
Markdown</a> syntax is supported
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *, help_text=help_text, required=False, **kwargs):
|
||||||
|
super().__init__(help_text=help_text, required=required, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SlugField(forms.SlugField):
|
||||||
|
"""
|
||||||
|
Extend Django's built-in SlugField to automatically populate from a field called `name` unless otherwise specified.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
slug_source: Name of the form field from which the slug value will be derived
|
||||||
|
"""
|
||||||
|
widget = widgets.SlugWidget
|
||||||
|
help_text = "URL-friendly unique shorthand"
|
||||||
|
|
||||||
|
def __init__(self, *, slug_source='name', help_text=help_text, **kwargs):
|
||||||
|
super().__init__(help_text=help_text, **kwargs)
|
||||||
|
|
||||||
|
self.widget.attrs['slug-source'] = slug_source
|
||||||
|
|
||||||
|
|
||||||
|
class ColorField(forms.CharField):
|
||||||
|
"""
|
||||||
|
A field which represents a color value in hexadecimal `RRGGBB` format. Utilizes NetBox's `ColorSelect` widget to
|
||||||
|
render choices.
|
||||||
|
"""
|
||||||
|
widget = widgets.ColorSelect
|
||||||
|
|
||||||
|
|
||||||
|
class TagFilterField(forms.MultipleChoiceField):
|
||||||
|
"""
|
||||||
|
A filter field for the tags of a model. Only the tags used by a model are displayed.
|
||||||
|
|
||||||
|
:param model: The model of the filter
|
||||||
|
"""
|
||||||
|
widget = widgets.StaticSelectMultiple
|
||||||
|
|
||||||
|
def __init__(self, model, *args, **kwargs):
|
||||||
|
def get_choices():
|
||||||
|
tags = model.tags.annotate(
|
||||||
|
count=Count('extras_taggeditem_items')
|
||||||
|
).order_by('name')
|
||||||
|
return [
|
||||||
|
(str(tag.slug), '{} ({})'.format(tag.name, tag.count)) for tag in tags
|
||||||
|
]
|
||||||
|
|
||||||
|
# Choices are fetched each time the form is initialized
|
||||||
|
super().__init__(label='Tags', choices=get_choices, required=False, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class LaxURLField(forms.URLField):
|
||||||
|
"""
|
||||||
|
Modifies Django's built-in URLField to remove the requirement for fully-qualified domain names
|
||||||
|
(e.g. http://myserver/ is valid)
|
||||||
|
"""
|
||||||
|
default_validators = [EnhancedURLValidator()]
|
||||||
|
|
||||||
|
|
||||||
|
class JSONField(_JSONField):
|
||||||
|
"""
|
||||||
|
Custom wrapper around Django's built-in JSONField to avoid presenting "null" as the default text.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if not self.help_text:
|
||||||
|
self.help_text = 'Enter context data in <a href="https://json.org/">JSON</a> format.'
|
||||||
|
self.widget.attrs['placeholder'] = ''
|
||||||
|
|
||||||
|
def prepare_value(self, value):
|
||||||
|
if isinstance(value, InvalidJSONInput):
|
||||||
|
return value
|
||||||
|
if value is None:
|
||||||
|
return ''
|
||||||
|
return json.dumps(value, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
class MACAddressField(forms.Field):
|
||||||
|
"""
|
||||||
|
Validates a 48-bit MAC address.
|
||||||
|
"""
|
||||||
|
widget = forms.CharField
|
||||||
|
default_error_messages = {
|
||||||
|
'invalid': 'MAC address must be in EUI-48 format',
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_python(self, value):
|
||||||
|
value = super().to_python(value)
|
||||||
|
|
||||||
|
# Validate MAC address format
|
||||||
|
try:
|
||||||
|
value = EUI(value.strip())
|
||||||
|
except AddrFormatError:
|
||||||
|
raise forms.ValidationError(self.error_messages['invalid'], code='invalid')
|
||||||
|
|
||||||
|
return value
|
@ -10,7 +10,7 @@ from .widgets import APISelect, APISelectMultiple, ClearableFileInput, StaticSel
|
|||||||
__all__ = (
|
__all__ = (
|
||||||
'BootstrapMixin',
|
'BootstrapMixin',
|
||||||
'BulkEditForm',
|
'BulkEditForm',
|
||||||
'BulkEditBaseForm',
|
'BulkEditMixin',
|
||||||
'BulkRenameForm',
|
'BulkRenameForm',
|
||||||
'ConfirmationForm',
|
'ConfirmationForm',
|
||||||
'CSVModelForm',
|
'CSVModelForm',
|
||||||
@ -21,6 +21,10 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Mixins
|
||||||
|
#
|
||||||
|
|
||||||
class BootstrapMixin:
|
class BootstrapMixin:
|
||||||
"""
|
"""
|
||||||
Add the base Bootstrap CSS classes to form elements.
|
Add the base Bootstrap CSS classes to form elements.
|
||||||
@ -61,6 +65,21 @@ class BootstrapMixin:
|
|||||||
field.widget.attrs['class'] = ' '.join((css, 'form-select')).strip()
|
field.widget.attrs['class'] = ' '.join((css, 'form-select')).strip()
|
||||||
|
|
||||||
|
|
||||||
|
class BulkEditMixin:
|
||||||
|
"""
|
||||||
|
Base form for editing multiple objects in bulk
|
||||||
|
"""
|
||||||
|
nullable_fields = ()
|
||||||
|
|
||||||
|
def __init__(self, model, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.model = model
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Form classes
|
||||||
|
#
|
||||||
|
|
||||||
class ReturnURLForm(forms.Form):
|
class ReturnURLForm(forms.Form):
|
||||||
"""
|
"""
|
||||||
Provides a hidden return URL field to control where the user is directed after the form is submitted.
|
Provides a hidden return URL field to control where the user is directed after the form is submitted.
|
||||||
@ -75,21 +94,7 @@ class ConfirmationForm(BootstrapMixin, ReturnURLForm):
|
|||||||
confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
|
confirm = forms.BooleanField(required=True, widget=forms.HiddenInput(), initial=True)
|
||||||
|
|
||||||
|
|
||||||
class BulkEditBaseForm(forms.Form):
|
class BulkEditForm(BootstrapMixin, BulkEditMixin, forms.Form):
|
||||||
"""
|
|
||||||
Base form for editing multiple objects in bulk
|
|
||||||
"""
|
|
||||||
def __init__(self, model, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
self.model = model
|
|
||||||
self.nullable_fields = []
|
|
||||||
|
|
||||||
# Copy any nullable fields defined in Meta
|
|
||||||
if hasattr(self.Meta, 'nullable_fields'):
|
|
||||||
self.nullable_fields = self.Meta.nullable_fields
|
|
||||||
|
|
||||||
|
|
||||||
class BulkEditForm(BootstrapMixin, BulkEditBaseForm):
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ from django import forms
|
|||||||
from dcim.choices import InterfaceModeChoices
|
from dcim.choices import InterfaceModeChoices
|
||||||
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
|
from dcim.constants import INTERFACE_MTU_MAX, INTERFACE_MTU_MIN
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
add_blank_choice, BulkEditNullBooleanSelect, BulkRenameForm, CommentField, DynamicModelChoiceField,
|
add_blank_choice, BulkEditNullBooleanSelect, BulkRenameForm, CommentField, DynamicModelChoiceField,
|
||||||
@ -23,7 +23,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ClusterTypeBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -33,11 +33,10 @@ class ClusterTypeBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ClusterGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -47,11 +46,10 @@ class ClusterGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('description',)
|
||||||
nullable_fields = ['description']
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class ClusterBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -89,13 +87,12 @@ class ClusterBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'group', 'site', 'comments', 'tenant',
|
||||||
'group', 'site', 'comments', 'tenant',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VirtualMachineBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -144,13 +141,12 @@ class VirtualMachineBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|||||||
label='Comments'
|
label='Comments'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
||||||
'role', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class VMInterfaceBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=VMInterface.objects.all(),
|
queryset=VMInterface.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput()
|
widget=forms.MultipleHiddenInput()
|
||||||
@ -197,10 +193,9 @@ class VMInterfaceBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = [
|
'parent', 'bridge', 'mtu', 'description',
|
||||||
'parent', 'bridge', 'mtu', 'description',
|
)
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from dcim.choices import InterfaceModeChoices
|
from dcim.choices import InterfaceModeChoices
|
||||||
from dcim.models import DeviceRole, Platform, Site
|
from dcim.models import DeviceRole, Platform, Site
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from tenancy.models import Tenant
|
from tenancy.models import Tenant
|
||||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
from virtualization.choices import *
|
from virtualization.choices import *
|
||||||
@ -15,7 +15,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterTypeCSVForm(CustomFieldModelCSVForm):
|
class ClusterTypeCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -23,7 +23,7 @@ class ClusterTypeCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'description')
|
fields = ('name', 'slug', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupCSVForm(CustomFieldModelCSVForm):
|
class ClusterGroupCSVForm(NetBoxModelCSVForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -31,7 +31,7 @@ class ClusterGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'description')
|
fields = ('name', 'slug', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ClusterCSVForm(CustomFieldModelCSVForm):
|
class ClusterCSVForm(NetBoxModelCSVForm):
|
||||||
type = CSVModelChoiceField(
|
type = CSVModelChoiceField(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
to_field_name='name',
|
to_field_name='name',
|
||||||
@ -61,7 +61,7 @@ class ClusterCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'type', 'group', 'site', 'comments')
|
fields = ('name', 'type', 'group', 'site', 'comments')
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineCSVForm(CustomFieldModelCSVForm):
|
class VirtualMachineCSVForm(NetBoxModelCSVForm):
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=VirtualMachineStatusChoices,
|
choices=VirtualMachineStatusChoices,
|
||||||
help_text='Operational status of device'
|
help_text='Operational status of device'
|
||||||
@ -99,7 +99,7 @@ class VirtualMachineCSVForm(CustomFieldModelCSVForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceCSVForm(CustomFieldModelCSVForm):
|
class VMInterfaceCSVForm(NetBoxModelCSVForm):
|
||||||
virtual_machine = CSVModelChoiceField(
|
virtual_machine = CSVModelChoiceField(
|
||||||
queryset=VirtualMachine.objects.all(),
|
queryset=VirtualMachine.objects.all(),
|
||||||
to_field_name='name'
|
to_field_name='name'
|
||||||
|
@ -2,7 +2,8 @@ from django import forms
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
from dcim.models import DeviceRole, Platform, Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelFilterForm, LocalConfigContextFilterForm
|
from extras.forms import LocalConfigContextFilterForm
|
||||||
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from tenancy.forms import TenancyFilterForm
|
from tenancy.forms import TenancyFilterForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
DynamicModelMultipleChoiceField, StaticSelect, StaticSelectMultiple, TagFilterField, BOOLEAN_WITH_BLANK_CHOICES,
|
||||||
@ -19,24 +20,24 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterTypeFilterForm(CustomFieldModelFilterForm):
|
class ClusterTypeFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ClusterType
|
model = ClusterType
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupFilterForm(CustomFieldModelFilterForm):
|
class ClusterGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = ClusterGroup
|
model = ClusterGroup
|
||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
class ClusterFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = Cluster
|
model = Cluster
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['group_id', 'type_id'],
|
('Attributes', ('group_id', 'type_id')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
type_id = DynamicModelMultipleChoiceField(
|
type_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterType.objects.all(),
|
queryset=ClusterType.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -71,15 +72,15 @@ class ClusterFilterForm(TenancyFilterForm, CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, CustomFieldModelFilterForm):
|
class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm, NetBoxModelFilterSetForm):
|
||||||
model = VirtualMachine
|
model = VirtualMachine
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['cluster_group_id', 'cluster_type_id', 'cluster_id'],
|
('Cluster', ('cluster_group_id', 'cluster_type_id', 'cluster_id')),
|
||||||
['region_id', 'site_group_id', 'site_id'],
|
('Location', ('region_id', 'site_group_id', 'site_id')),
|
||||||
['status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data'],
|
('Attriubtes', ('status', 'role_id', 'platform_id', 'mac_address', 'has_primary_ip', 'local_context_data')),
|
||||||
['tenant_group_id', 'tenant_id'],
|
('Tenant', ('tenant_group_id', 'tenant_id')),
|
||||||
]
|
)
|
||||||
cluster_group_id = DynamicModelMultipleChoiceField(
|
cluster_group_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -151,13 +152,13 @@ class VirtualMachineFilterForm(LocalConfigContextFilterForm, TenancyFilterForm,
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceFilterForm(CustomFieldModelFilterForm):
|
class VMInterfaceFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = VMInterface
|
model = VMInterface
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
['q', 'tag'],
|
(None, ('q', 'tag')),
|
||||||
['cluster_id', 'virtual_machine_id'],
|
('Virtual Machine', ('cluster_id', 'virtual_machine_id')),
|
||||||
['enabled', 'mac_address'],
|
('Attributes', ('enabled', 'mac_address')),
|
||||||
]
|
)
|
||||||
cluster_id = DynamicModelMultipleChoiceField(
|
cluster_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=Cluster.objects.all(),
|
queryset=Cluster.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -5,9 +5,9 @@ from django.core.exceptions import ValidationError
|
|||||||
from dcim.forms.common import InterfaceCommonForm
|
from dcim.forms.common import InterfaceCommonForm
|
||||||
from dcim.forms.models import INTERFACE_MODE_HELP_TEXT
|
from dcim.forms.models import INTERFACE_MODE_HELP_TEXT
|
||||||
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup
|
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site, SiteGroup
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.models import IPAddress, VLAN, VLANGroup
|
from ipam.models import IPAddress, VLAN, VLANGroup
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from tenancy.forms import TenancyForm
|
from tenancy.forms import TenancyForm
|
||||||
from utilities.forms import (
|
from utilities.forms import (
|
||||||
BootstrapMixin, CommentField, ConfirmationForm, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
BootstrapMixin, CommentField, ConfirmationForm, DynamicModelChoiceField, DynamicModelMultipleChoiceField,
|
||||||
@ -26,7 +26,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterTypeForm(CustomFieldModelForm):
|
class ClusterTypeForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -40,7 +40,7 @@ class ClusterTypeForm(CustomFieldModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterGroupForm(CustomFieldModelForm):
|
class ClusterGroupForm(NetBoxModelForm):
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
tags = DynamicModelMultipleChoiceField(
|
tags = DynamicModelMultipleChoiceField(
|
||||||
queryset=Tag.objects.all(),
|
queryset=Tag.objects.all(),
|
||||||
@ -54,7 +54,7 @@ class ClusterGroupForm(CustomFieldModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClusterForm(TenancyForm, CustomFieldModelForm):
|
class ClusterForm(TenancyForm, NetBoxModelForm):
|
||||||
type = DynamicModelChoiceField(
|
type = DynamicModelChoiceField(
|
||||||
queryset=ClusterType.objects.all()
|
queryset=ClusterType.objects.all()
|
||||||
)
|
)
|
||||||
@ -90,15 +90,16 @@ class ClusterForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Cluster', ('name', 'type', 'group', 'region', 'site_group', 'site', 'tags')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Cluster
|
model = Cluster
|
||||||
fields = (
|
fields = (
|
||||||
'name', 'type', 'group', 'tenant', 'region', 'site_group', 'site', 'comments', 'tags',
|
'name', 'type', 'group', 'tenant', 'region', 'site_group', 'site', 'comments', 'tags',
|
||||||
)
|
)
|
||||||
fieldsets = (
|
|
||||||
('Cluster', ('name', 'type', 'group', 'region', 'site_group', 'site', 'tags')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ClusterAddDevicesForm(BootstrapMixin, forms.Form):
|
class ClusterAddDevicesForm(BootstrapMixin, forms.Form):
|
||||||
@ -171,7 +172,7 @@ class ClusterRemoveDevicesForm(ConfirmationForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VirtualMachineForm(TenancyForm, CustomFieldModelForm):
|
class VirtualMachineForm(TenancyForm, NetBoxModelForm):
|
||||||
cluster_group = DynamicModelChoiceField(
|
cluster_group = DynamicModelChoiceField(
|
||||||
queryset=ClusterGroup.objects.all(),
|
queryset=ClusterGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -206,20 +207,21 @@ class VirtualMachineForm(TenancyForm, CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Virtual Machine', ('name', 'role', 'status', 'tags')),
|
||||||
|
('Cluster', ('cluster_group', 'cluster')),
|
||||||
|
('Tenancy', ('tenant_group', 'tenant')),
|
||||||
|
('Management', ('platform', 'primary_ip4', 'primary_ip6')),
|
||||||
|
('Resources', ('vcpus', 'memory', 'disk')),
|
||||||
|
('Config Context', ('local_context_data',)),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VirtualMachine
|
model = VirtualMachine
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
|
'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant_group', 'tenant', 'platform', 'primary_ip4',
|
||||||
'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data',
|
'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Virtual Machine', ('name', 'role', 'status', 'tags')),
|
|
||||||
('Cluster', ('cluster_group', 'cluster')),
|
|
||||||
('Tenancy', ('tenant_group', 'tenant')),
|
|
||||||
('Management', ('platform', 'primary_ip4', 'primary_ip6')),
|
|
||||||
('Resources', ('vcpus', 'memory', 'disk')),
|
|
||||||
('Config Context', ('local_context_data',)),
|
|
||||||
)
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'local_context_data': "Local config context data overwrites all sources contexts in the final rendered "
|
'local_context_data': "Local config context data overwrites all sources contexts in the final rendered "
|
||||||
"config context",
|
"config context",
|
||||||
@ -271,7 +273,7 @@ class VirtualMachineForm(TenancyForm, CustomFieldModelForm):
|
|||||||
self.fields['primary_ip6'].widget.attrs['readonly'] = True
|
self.fields['primary_ip6'].widget.attrs['readonly'] = True
|
||||||
|
|
||||||
|
|
||||||
class VMInterfaceForm(InterfaceCommonForm, CustomFieldModelForm):
|
class VMInterfaceForm(InterfaceCommonForm, NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=VMInterface.objects.all(),
|
queryset=VMInterface.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from extras.forms import AddRemoveTagsForm, CustomFieldModelBulkEditForm
|
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
|
from netbox.forms import NetBoxModelBulkEditForm
|
||||||
from utilities.forms import add_blank_choice, DynamicModelChoiceField
|
from utilities.forms import add_blank_choice, DynamicModelChoiceField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.constants import SSID_MAX_LENGTH
|
from wireless.constants import SSID_MAX_LENGTH
|
||||||
@ -15,7 +15,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -29,11 +29,10 @@ class WirelessLANGroupBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditFo
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = ('parent', 'description')
|
||||||
nullable_fields = ['parent', 'description']
|
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class WirelessLANBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=WirelessLAN.objects.all(),
|
queryset=WirelessLAN.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -68,11 +67,12 @@ class WirelessLANBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Pre-shared key'
|
label='Pre-shared key'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = ['ssid', 'group', 'vlan', 'description', 'auth_type', 'auth_cipher', 'auth_psk']
|
'ssid', 'group', 'vlan', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
pk = forms.ModelMultipleChoiceField(
|
pk = forms.ModelMultipleChoiceField(
|
||||||
queryset=WirelessLink.objects.all(),
|
queryset=WirelessLink.objects.all(),
|
||||||
widget=forms.MultipleHiddenInput
|
widget=forms.MultipleHiddenInput
|
||||||
@ -102,5 +102,6 @@ class WirelessLinkBulkEditForm(AddRemoveTagsForm, CustomFieldModelBulkEditForm):
|
|||||||
label='Pre-shared key'
|
label='Pre-shared key'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
nullable_fields = (
|
||||||
nullable_fields = ['ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk']
|
'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk',
|
||||||
|
)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from dcim.models import Interface
|
from dcim.models import Interface
|
||||||
from extras.forms import CustomFieldModelCSVForm
|
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
|
from netbox.forms import NetBoxModelCSVForm
|
||||||
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
@ -13,7 +13,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANGroupCSVForm(CustomFieldModelCSVForm):
|
class WirelessLANGroupCSVForm(NetBoxModelCSVForm):
|
||||||
parent = CSVModelChoiceField(
|
parent = CSVModelChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -27,7 +27,7 @@ class WirelessLANGroupCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('name', 'slug', 'parent', 'description')
|
fields = ('name', 'slug', 'parent', 'description')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANCSVForm(CustomFieldModelCSVForm):
|
class WirelessLANCSVForm(NetBoxModelCSVForm):
|
||||||
group = CSVModelChoiceField(
|
group = CSVModelChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -56,7 +56,7 @@ class WirelessLANCSVForm(CustomFieldModelCSVForm):
|
|||||||
fields = ('ssid', 'group', 'description', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk')
|
fields = ('ssid', 'group', 'description', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk')
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkCSVForm(CustomFieldModelCSVForm):
|
class WirelessLinkCSVForm(NetBoxModelCSVForm):
|
||||||
status = CSVChoiceField(
|
status = CSVChoiceField(
|
||||||
choices=LinkStatusChoices,
|
choices=LinkStatusChoices,
|
||||||
help_text='Connection status'
|
help_text='Connection status'
|
||||||
|
@ -2,7 +2,7 @@ from django import forms
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from dcim.choices import LinkStatusChoices
|
from dcim.choices import LinkStatusChoices
|
||||||
from extras.forms import CustomFieldModelFilterForm
|
from netbox.forms import NetBoxModelFilterSetForm
|
||||||
from utilities.forms import add_blank_choice, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField
|
from utilities.forms import add_blank_choice, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField
|
||||||
from wireless.choices import *
|
from wireless.choices import *
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
@ -14,7 +14,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANGroupFilterForm(CustomFieldModelFilterForm):
|
class WirelessLANGroupFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = WirelessLANGroup
|
model = WirelessLANGroup
|
||||||
parent_id = DynamicModelMultipleChoiceField(
|
parent_id = DynamicModelMultipleChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
@ -24,12 +24,13 @@ class WirelessLANGroupFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANFilterForm(CustomFieldModelFilterForm):
|
class WirelessLANFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
field_groups = [
|
fieldsets = (
|
||||||
('q', 'tag'),
|
(None, ('q', 'tag')),
|
||||||
('group_id',),
|
('Attributes', ('ssid', 'group_id',)),
|
||||||
]
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
|
)
|
||||||
ssid = forms.CharField(
|
ssid = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
label='SSID'
|
label='SSID'
|
||||||
@ -56,7 +57,7 @@ class WirelessLANFilterForm(CustomFieldModelFilterForm):
|
|||||||
tag = TagFilterField(model)
|
tag = TagFilterField(model)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkFilterForm(CustomFieldModelFilterForm):
|
class WirelessLinkFilterForm(NetBoxModelFilterSetForm):
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
ssid = forms.CharField(
|
ssid = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
from dcim.models import Device, Interface, Location, Site
|
from dcim.models import Device, Interface, Location, Site
|
||||||
from extras.forms import CustomFieldModelForm
|
|
||||||
from extras.models import Tag
|
from extras.models import Tag
|
||||||
from ipam.models import VLAN
|
from ipam.models import VLAN
|
||||||
|
from netbox.forms import NetBoxModelForm
|
||||||
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, StaticSelect
|
from utilities.forms import DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, StaticSelect
|
||||||
from wireless.models import *
|
from wireless.models import *
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANGroupForm(CustomFieldModelForm):
|
class WirelessLANGroupForm(NetBoxModelForm):
|
||||||
parent = DynamicModelChoiceField(
|
parent = DynamicModelChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -30,7 +30,7 @@ class WirelessLANGroupForm(CustomFieldModelForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class WirelessLANForm(CustomFieldModelForm):
|
class WirelessLANForm(NetBoxModelForm):
|
||||||
group = DynamicModelChoiceField(
|
group = DynamicModelChoiceField(
|
||||||
queryset=WirelessLANGroup.objects.all(),
|
queryset=WirelessLANGroup.objects.all(),
|
||||||
required=False
|
required=False
|
||||||
@ -45,23 +45,24 @@ class WirelessLANForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
|
||||||
|
('VLAN', ('vlan',)),
|
||||||
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLAN
|
model = WirelessLAN
|
||||||
fields = [
|
fields = [
|
||||||
'ssid', 'group', 'description', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
'ssid', 'group', 'description', 'vlan', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
|
|
||||||
('VLAN', ('vlan',)),
|
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'auth_type': StaticSelect,
|
'auth_type': StaticSelect,
|
||||||
'auth_cipher': StaticSelect,
|
'auth_cipher': StaticSelect,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class WirelessLinkForm(CustomFieldModelForm):
|
class WirelessLinkForm(NetBoxModelForm):
|
||||||
site_a = DynamicModelChoiceField(
|
site_a = DynamicModelChoiceField(
|
||||||
queryset=Site.objects.all(),
|
queryset=Site.objects.all(),
|
||||||
required=False,
|
required=False,
|
||||||
@ -141,18 +142,19 @@ class WirelessLinkForm(CustomFieldModelForm):
|
|||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
fieldsets = (
|
||||||
|
('Side A', ('site_a', 'location_a', 'device_a', 'interface_a')),
|
||||||
|
('Side B', ('site_b', 'location_b', 'device_b', 'interface_b')),
|
||||||
|
('Link', ('status', 'ssid', 'description', 'tags')),
|
||||||
|
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WirelessLink
|
model = WirelessLink
|
||||||
fields = [
|
fields = [
|
||||||
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
|
'site_a', 'location_a', 'device_a', 'interface_a', 'site_b', 'location_b', 'device_b', 'interface_b',
|
||||||
'status', 'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
'status', 'ssid', 'description', 'auth_type', 'auth_cipher', 'auth_psk', 'tags',
|
||||||
]
|
]
|
||||||
fieldsets = (
|
|
||||||
('Side A', ('site_a', 'location_a', 'device_a', 'interface_a')),
|
|
||||||
('Side B', ('site_b', 'location_b', 'device_b', 'interface_b')),
|
|
||||||
('Link', ('status', 'ssid', 'description', 'tags')),
|
|
||||||
('Authentication', ('auth_type', 'auth_cipher', 'auth_psk')),
|
|
||||||
)
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'status': StaticSelect,
|
'status': StaticSelect,
|
||||||
'auth_type': StaticSelect,
|
'auth_type': StaticSelect,
|
||||||
|
Loading…
Reference in New Issue
Block a user