Merge pull request #8504 from netbox-community/8488-plugins-forms

Closes #8488: Support form components for plugins
This commit is contained in:
Jeremy Stretch 2022-01-31 16:46:33 -05:00 committed by GitHub
commit 4347f624d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1625 additions and 1567 deletions

View 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

View File

@ -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'

View File

@ -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', )
]

View File

@ -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',

View File

@ -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,

View File

@ -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",

View File

@ -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']

View File

@ -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',

View File

@ -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',

View File

@ -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)

View File

@ -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(),

View File

@ -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,

View File

@ -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 = []

View File

@ -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)

View File

@ -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'),

View File

@ -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):

View File

@ -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):

View File

@ -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,

View File

@ -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),

View File

@ -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

View File

@ -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
View 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)

View File

@ -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
# #

View File

@ -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>

View File

@ -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" />

View File

@ -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>

View File

@ -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']

View File

@ -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,

View File

@ -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,

View File

@ -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}),
} }

View File

@ -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):

View File

@ -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)

View File

@ -0,0 +1,5 @@
from .content_types import *
from .csv import *
from .dynamic import *
from .expandable import *
from .fields import *

View 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

View 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)

View 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)

View 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]

View 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

View File

@ -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

View File

@ -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)

View File

@ -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'

View File

@ -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,

View File

@ -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,

View File

@ -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',
)

View File

@ -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'

View File

@ -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,

View File

@ -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,