diff --git a/netbox/netbox/navigation_menu.py b/netbox/netbox/navigation_menu.py
index b3e11f6ce..7f64a2df8 100644
--- a/netbox/netbox/navigation_menu.py
+++ b/netbox/netbox/navigation_menu.py
@@ -168,6 +168,7 @@ CONNECTIONS_MENU = Menu(
label='Connections',
items=(
get_model_item('dcim', 'cable', 'Cables', actions=['import']),
+ get_model_item('wireless', 'wirelesslink', 'Wirelesss Links', actions=['import']),
MenuItem(
link='dcim:interface_connections_list',
link_text='Interface Connections',
@@ -196,7 +197,7 @@ WIRELESS_MENU = Menu(
label='Wireless',
items=(
get_model_item('wireless', 'wirelesslan', 'Wireless LANs'),
- get_model_item('wireless', 'wirelesslink', 'Wirelesss Links', actions=['import']),
+ get_model_item('wireless', 'wirelesslangroup', 'Wireless LAN Groups'),
),
),
),
diff --git a/netbox/templates/wireless/wirelesslan.html b/netbox/templates/wireless/wirelesslan.html
index f8fabf558..f2133cd54 100644
--- a/netbox/templates/wireless/wirelesslan.html
+++ b/netbox/templates/wireless/wirelesslan.html
@@ -13,6 +13,16 @@
SSID |
{{ object.ssid }} |
+
+ Group |
+
+ {% if object.group %}
+ {{ object.group }}
+ {% else %}
+ None
+ {% endif %}
+ |
+
Description |
{{ object.description|placeholder }} |
diff --git a/netbox/templates/wireless/wirelesslangroup.html b/netbox/templates/wireless/wirelesslangroup.html
new file mode 100644
index 000000000..170f72eff
--- /dev/null
+++ b/netbox/templates/wireless/wirelesslangroup.html
@@ -0,0 +1,72 @@
+{% extends 'generic/object.html' %}
+{% load helpers %}
+{% load plugins %}
+
+{% block breadcrumbs %}
+ {{ block.super }}
+ {% for group in object.get_ancestors %}
+ {{ group }}
+ {% endfor %}
+{% endblock %}
+
+{% block content %}
+
+
+
+ {% plugin_left_page object %}
+
+
+ {% include 'inc/custom_fields_panel.html' %}
+ {% plugin_right_page object %}
+
+
+
+
+
+
+
+ {% include 'inc/table.html' with table=wirelesslans_table %}
+
+ {% if perms.wireless.add_wirelesslan %}
+
+ {% endif %}
+
+ {% include 'inc/paginator.html' with paginator=wirelesslans_table.paginator page=wirelesslans_table.page %}
+ {% plugin_full_width_page object %}
+
+
+{% endblock %}
diff --git a/netbox/wireless/api/nested_serializers.py b/netbox/wireless/api/nested_serializers.py
index 5a8cf6671..e9a840bfc 100644
--- a/netbox/wireless/api/nested_serializers.py
+++ b/netbox/wireless/api/nested_serializers.py
@@ -5,10 +5,21 @@ from wireless.models import *
__all__ = (
'NestedWirelessLANSerializer',
+ 'NestedWirelessLANGroupSerializer',
'NestedWirelessLinkSerializer',
)
+class NestedWirelessLANGroupSerializer(WritableNestedSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
+ wirelesslan_count = serializers.IntegerField(read_only=True)
+ _depth = serializers.IntegerField(source='level', read_only=True)
+
+ class Meta:
+ model = WirelessLANGroup
+ fields = ['id', 'url', 'display', 'name', 'slug', 'wirelesslan_count', '_depth']
+
+
class NestedWirelessLANSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
diff --git a/netbox/wireless/api/serializers.py b/netbox/wireless/api/serializers.py
index 5a7330129..24395b77c 100644
--- a/netbox/wireless/api/serializers.py
+++ b/netbox/wireless/api/serializers.py
@@ -4,7 +4,7 @@ from dcim.choices import LinkStatusChoices
from dcim.api.serializers import NestedInterfaceSerializer
from ipam.api.serializers import NestedVLANSerializer
from netbox.api import ChoiceField
-from netbox.api.serializers import PrimaryModelSerializer
+from netbox.api.serializers import NestedGroupModelSerializer, PrimaryModelSerializer
from wireless.models import *
from .nested_serializers import *
@@ -14,6 +14,19 @@ __all__ = (
)
+class WirelessLANGroupSerializer(NestedGroupModelSerializer):
+ url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslangroup-detail')
+ parent = NestedWirelessLANGroupSerializer(required=False, allow_null=True)
+ wirelesslan_count = serializers.IntegerField(read_only=True)
+
+ class Meta:
+ model = WirelessLANGroup
+ fields = [
+ 'id', 'url', 'display', 'name', 'slug', 'parent', 'description', 'custom_fields', 'created', 'last_updated',
+ 'wirelesslan_count', '_depth',
+ ]
+
+
class WirelessLANSerializer(PrimaryModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='wireless-api:wirelesslan-detail')
vlan = NestedVLANSerializer(required=False, allow_null=True)
diff --git a/netbox/wireless/api/urls.py b/netbox/wireless/api/urls.py
index 431bb05f8..54f764db6 100644
--- a/netbox/wireless/api/urls.py
+++ b/netbox/wireless/api/urls.py
@@ -5,6 +5,7 @@ from . import views
router = OrderedDefaultRouter()
router.APIRootView = views.WirelessRootView
+router.register('wireless-lan-groupss', views.WirelessLANGroupViewSet)
router.register('wireless-lans', views.WirelessLANViewSet)
router.register('wireless-links', views.WirelessLinkViewSet)
diff --git a/netbox/wireless/api/views.py b/netbox/wireless/api/views.py
index aa361a7f7..734f6940f 100644
--- a/netbox/wireless/api/views.py
+++ b/netbox/wireless/api/views.py
@@ -14,6 +14,18 @@ class WirelessRootView(APIRootView):
return 'Wireless'
+class WirelessLANGroupViewSet(CustomFieldModelViewSet):
+ queryset = WirelessLANGroup.objects.add_related_count(
+ WirelessLANGroup.objects.all(),
+ WirelessLAN,
+ 'group',
+ 'wirelesslan_count',
+ cumulative=True
+ )
+ serializer_class = serializers.WirelessLANGroupSerializer
+ filterset_class = filtersets.WirelessLANGroupFilterSet
+
+
class WirelessLANViewSet(CustomFieldModelViewSet):
queryset = WirelessLAN.objects.prefetch_related('vlan', 'tags')
serializer_class = serializers.WirelessLANSerializer
diff --git a/netbox/wireless/filtersets.py b/netbox/wireless/filtersets.py
index f765172dd..ac503e474 100644
--- a/netbox/wireless/filtersets.py
+++ b/netbox/wireless/filtersets.py
@@ -3,15 +3,31 @@ from django.db.models import Q
from dcim.choices import LinkStatusChoices
from extras.filters import TagFilter
-from netbox.filtersets import PrimaryModelFilterSet
+from netbox.filtersets import OrganizationalModelFilterSet, PrimaryModelFilterSet
from .models import *
__all__ = (
'WirelessLANFilterSet',
+ 'WirelessLANGroupFilterSet',
'WirelessLinkFilterSet',
)
+class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
+ parent_id = django_filters.ModelMultipleChoiceFilter(
+ queryset=WirelessLANGroup.objects.all()
+ )
+ parent = django_filters.ModelMultipleChoiceFilter(
+ field_name='parent__slug',
+ queryset=WirelessLANGroup.objects.all(),
+ to_field_name='slug'
+ )
+
+ class Meta:
+ model = WirelessLANGroup
+ fields = ['id', 'name', 'slug', 'description']
+
+
class WirelessLANFilterSet(PrimaryModelFilterSet):
q = django_filters.CharFilter(
method='search',
diff --git a/netbox/wireless/forms/bulk_edit.py b/netbox/wireless/forms/bulk_edit.py
index 72af81f56..c0d5a925e 100644
--- a/netbox/wireless/forms/bulk_edit.py
+++ b/netbox/wireless/forms/bulk_edit.py
@@ -9,15 +9,38 @@ from wireless.models import *
__all__ = (
'WirelessLANBulkEditForm',
+ 'WirelessLANGroupBulkEditForm',
'WirelessLinkBulkEditForm',
)
+class WirelessLANGroupBulkEditForm(BootstrapMixin, CustomFieldModelBulkEditForm):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ widget=forms.MultipleHiddenInput
+ )
+ parent = DynamicModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False
+ )
+ description = forms.CharField(
+ max_length=200,
+ required=False
+ )
+
+ class Meta:
+ nullable_fields = ['parent', 'description']
+
+
class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
pk = forms.ModelMultipleChoiceField(
queryset=WirelessLAN.objects.all(),
widget=forms.MultipleHiddenInput
)
+ group = DynamicModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False
+ )
vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False,
@@ -31,7 +54,7 @@ class WirelessLANBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldMode
)
class Meta:
- nullable_fields = ['vlan', 'ssid', 'description']
+ nullable_fields = ['ssid', 'group', 'vlan', 'description']
class WirelessLinkBulkEditForm(BootstrapMixin, AddRemoveTagsForm, CustomFieldModelBulkEditForm):
diff --git a/netbox/wireless/forms/bulk_import.py b/netbox/wireless/forms/bulk_import.py
index 763305c38..6b22728f6 100644
--- a/netbox/wireless/forms/bulk_import.py
+++ b/netbox/wireless/forms/bulk_import.py
@@ -2,16 +2,37 @@ from dcim.choices import LinkStatusChoices
from dcim.models import Interface
from extras.forms import CustomFieldModelCSVForm
from ipam.models import VLAN
-from utilities.forms import CSVChoiceField, CSVModelChoiceField
+from utilities.forms import CSVChoiceField, CSVModelChoiceField, SlugField
from wireless.models import *
__all__ = (
'WirelessLANCSVForm',
+ 'WirelessLANGroupCSVForm',
'WirelessLinkCSVForm',
)
+class WirelessLANGroupCSVForm(CustomFieldModelCSVForm):
+ parent = CSVModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text='Parent group'
+ )
+ slug = SlugField()
+
+ class Meta:
+ model = WirelessLANGroup
+ fields = ('name', 'slug', 'parent', 'description')
+
+
class WirelessLANCSVForm(CustomFieldModelCSVForm):
+ group = CSVModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False,
+ to_field_name='name',
+ help_text='Assigned group'
+ )
vlan = CSVModelChoiceField(
queryset=VLAN.objects.all(),
to_field_name='name',
@@ -20,7 +41,7 @@ class WirelessLANCSVForm(CustomFieldModelCSVForm):
class Meta:
model = WirelessLAN
- fields = ('ssid', 'description', 'vlan')
+ fields = ('ssid', 'group', 'description', 'vlan')
class WirelessLinkCSVForm(CustomFieldModelCSVForm):
diff --git a/netbox/wireless/forms/filtersets.py b/netbox/wireless/forms/filtersets.py
index 6da3cd716..13aae99a5 100644
--- a/netbox/wireless/forms/filtersets.py
+++ b/netbox/wireless/forms/filtersets.py
@@ -3,19 +3,38 @@ from django.utils.translation import gettext as _
from dcim.choices import LinkStatusChoices
from extras.forms import CustomFieldModelFilterForm
-from utilities.forms import add_blank_choice, BootstrapMixin, StaticSelect, TagFilterField
+from utilities.forms import (
+ add_blank_choice, BootstrapMixin, DynamicModelMultipleChoiceField, StaticSelect, TagFilterField,
+)
from wireless.models import *
__all__ = (
'WirelessLANFilterForm',
+ 'WirelessLANGroupFilterForm',
'WirelessLinkFilterForm',
)
+class WirelessLANGroupFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
+ model = WirelessLANGroup
+ q = forms.CharField(
+ required=False,
+ widget=forms.TextInput(attrs={'placeholder': _('All Fields')}),
+ label=_('Search')
+ )
+ parent_id = DynamicModelMultipleChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False,
+ label=_('Parent group'),
+ fetch_trigger='open'
+ )
+
+
class WirelessLANFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
model = WirelessLAN
field_groups = [
- ['q', 'tag'],
+ ('q', 'tag'),
+ ('group_id',),
]
q = forms.CharField(
required=False,
@@ -26,6 +45,13 @@ class WirelessLANFilterForm(BootstrapMixin, CustomFieldModelFilterForm):
required=False,
label='SSID'
)
+ group_id = DynamicModelMultipleChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False,
+ null_option='None',
+ label=_('Group'),
+ fetch_trigger='open'
+ )
tag = TagFilterField(model)
diff --git a/netbox/wireless/forms/models.py b/netbox/wireless/forms/models.py
index 174eb5983..ca20b6ea8 100644
--- a/netbox/wireless/forms/models.py
+++ b/netbox/wireless/forms/models.py
@@ -2,16 +2,37 @@ from dcim.models import Interface
from extras.forms import CustomFieldModelForm
from extras.models import Tag
from ipam.models import VLAN
-from utilities.forms import BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, StaticSelect
+from utilities.forms import (
+ BootstrapMixin, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField, StaticSelect,
+)
from wireless.models import *
__all__ = (
'WirelessLANForm',
+ 'WirelessLANGroupForm',
'WirelessLinkForm',
)
+class WirelessLANGroupForm(BootstrapMixin, CustomFieldModelForm):
+ parent = DynamicModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False
+ )
+ slug = SlugField()
+
+ class Meta:
+ model = WirelessLANGroup
+ fields = [
+ 'parent', 'name', 'slug', 'description',
+ ]
+
+
class WirelessLANForm(BootstrapMixin, CustomFieldModelForm):
+ group = DynamicModelChoiceField(
+ queryset=WirelessLANGroup.objects.all(),
+ required=False
+ )
vlan = DynamicModelChoiceField(
queryset=VLAN.objects.all(),
required=False
@@ -24,10 +45,10 @@ class WirelessLANForm(BootstrapMixin, CustomFieldModelForm):
class Meta:
model = WirelessLAN
fields = [
- 'ssid', 'description', 'vlan', 'tags',
+ 'ssid', 'group', 'description', 'vlan', 'tags',
]
fieldsets = (
- ('Wireless LAN', ('ssid', 'description', 'tags')),
+ ('Wireless LAN', ('ssid', 'group', 'description', 'tags')),
('VLAN', ('vlan',)),
)
diff --git a/netbox/wireless/graphql/schema.py b/netbox/wireless/graphql/schema.py
index 8297f4545..05fc57c4d 100644
--- a/netbox/wireless/graphql/schema.py
+++ b/netbox/wireless/graphql/schema.py
@@ -7,3 +7,9 @@ from .types import *
class WirelessQuery(graphene.ObjectType):
wirelesslan = ObjectField(WirelessLANType)
wirelesslan_list = ObjectListField(WirelessLANType)
+
+ wirelesslangroup = ObjectField(WirelessLANGroupType)
+ wirelesslangroup_list = ObjectListField(WirelessLANGroupType)
+
+ wirelesslink = ObjectField(WirelessLinkType)
+ wirelesslink_list = ObjectListField(WirelessLinkType)
diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py
index 0afd8e69a..4697cc44b 100644
--- a/netbox/wireless/graphql/types.py
+++ b/netbox/wireless/graphql/types.py
@@ -3,10 +3,19 @@ from netbox.graphql.types import ObjectType
__all__ = (
'WirelessLANType',
+ 'WirelessLANGroupType',
'WirelessLinkType',
)
+class WirelessLANGroupType(ObjectType):
+
+ class Meta:
+ model = models.WirelessLANGroup
+ fields = '__all__'
+ filterset_class = filtersets.WirelessLANGroupFilterSet
+
+
class WirelessLANType(ObjectType):
class Meta:
diff --git a/netbox/wireless/migrations/0001_wireless.py b/netbox/wireless/migrations/0001_wireless.py
index 8eb042d7d..068f4d64a 100644
--- a/netbox/wireless/migrations/0001_wireless.py
+++ b/netbox/wireless/migrations/0001_wireless.py
@@ -1,8 +1,7 @@
-# Generated by Django 3.2.8 on 2021-10-13 13:44
-
import django.core.serializers.json
from django.db import migrations, models
import django.db.models.deletion
+import mptt.fields
import taggit.managers
@@ -17,6 +16,27 @@ class Migration(migrations.Migration):
]
operations = [
+ migrations.CreateModel(
+ name='WirelessLANGroup',
+ fields=[
+ ('created', models.DateField(auto_now_add=True, null=True)),
+ ('last_updated', models.DateTimeField(auto_now=True, null=True)),
+ ('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
+ ('id', models.BigAutoField(primary_key=True, serialize=False)),
+ ('name', models.CharField(max_length=100, unique=True)),
+ ('slug', models.SlugField(max_length=100, unique=True)),
+ ('description', models.CharField(blank=True, max_length=200)),
+ ('lft', models.PositiveIntegerField(editable=False)),
+ ('rght', models.PositiveIntegerField(editable=False)),
+ ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+ ('level', models.PositiveIntegerField(editable=False)),
+ ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='wireless.wirelesslangroup')),
+ ],
+ options={
+ 'ordering': ('name', 'pk'),
+ 'unique_together': {('parent', 'name')},
+ },
+ ),
migrations.CreateModel(
name='WirelessLAN',
fields=[
@@ -25,6 +45,7 @@ class Migration(migrations.Migration):
('custom_field_data', models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder)),
('id', models.BigAutoField(primary_key=True, serialize=False)),
('ssid', models.CharField(max_length=32)),
+ ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wireless_lans', to='wireless.wirelesslangroup')),
('description', models.CharField(blank=True, max_length=200)),
('tags', taggit.managers.TaggableManager(through='extras.TaggedItem', to='extras.Tag')),
('vlan', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ipam.vlan')),
diff --git a/netbox/wireless/models.py b/netbox/wireless/models.py
index d02358f1c..12c4e55aa 100644
--- a/netbox/wireless/models.py
+++ b/netbox/wireless/models.py
@@ -1,20 +1,58 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
+from mptt.models import MPTTModel, TreeForeignKey
from dcim.choices import LinkStatusChoices
from dcim.constants import WIRELESS_IFACE_TYPES
from extras.utils import extras_features
-from netbox.models import BigIDModel, PrimaryModel
+from netbox.models import BigIDModel, NestedGroupModel, PrimaryModel
from utilities.querysets import RestrictedQuerySet
from .constants import SSID_MAX_LENGTH
__all__ = (
'WirelessLAN',
+ 'WirelessLANGroup',
'WirelessLink',
)
+@extras_features('custom_fields', 'custom_links', 'export_templates', 'webhooks')
+class WirelessLANGroup(NestedGroupModel):
+ """
+ A nested grouping of WirelessLANs
+ """
+ name = models.CharField(
+ max_length=100,
+ unique=True
+ )
+ slug = models.SlugField(
+ max_length=100,
+ unique=True
+ )
+ parent = TreeForeignKey(
+ to='self',
+ on_delete=models.CASCADE,
+ related_name='children',
+ blank=True,
+ null=True,
+ db_index=True
+ )
+ description = models.CharField(
+ max_length=200,
+ blank=True
+ )
+
+ class Meta:
+ ordering = ('name', 'pk')
+ unique_together = (
+ ('parent', 'name')
+ )
+
+ def get_absolute_url(self):
+ return reverse('wireless:wirelesslangroup', args=[self.pk])
+
+
@extras_features('custom_fields', 'custom_links', 'export_templates', 'tags', 'webhooks')
class WirelessLAN(PrimaryModel):
"""
@@ -24,6 +62,13 @@ class WirelessLAN(PrimaryModel):
max_length=SSID_MAX_LENGTH,
verbose_name='SSID'
)
+ group = models.ForeignKey(
+ to='wireless.WirelessLANGroup',
+ on_delete=models.SET_NULL,
+ related_name='wireless_lans',
+ blank=True,
+ null=True
+ )
vlan = models.ForeignKey(
to='ipam.VLAN',
on_delete=models.PROTECT,
@@ -100,7 +145,7 @@ class WirelessLink(PrimaryModel):
objects = RestrictedQuerySet.as_manager()
- clone_fields = ('ssid', 'status')
+ clone_fields = ('ssid', 'group', 'status')
class Meta:
ordering = ['pk']
diff --git a/netbox/wireless/tables.py b/netbox/wireless/tables.py
index 9b0ef7291..58d77b56f 100644
--- a/netbox/wireless/tables.py
+++ b/netbox/wireless/tables.py
@@ -1,14 +1,35 @@
import django_tables2 as tables
+from utilities.tables import (
+ BaseTable, ButtonsColumn, ChoiceFieldColumn, LinkedCountColumn, MPTTColumn, TagColumn, ToggleColumn,
+)
from .models import *
-from utilities.tables import BaseTable, ChoiceFieldColumn, TagColumn, ToggleColumn
__all__ = (
'WirelessLANTable',
+ 'WirelessLANGroupTable',
'WirelessLinkTable',
)
+class WirelessLANGroupTable(BaseTable):
+ pk = ToggleColumn()
+ name = MPTTColumn(
+ linkify=True
+ )
+ wirelesslan_count = LinkedCountColumn(
+ viewname='wireless:wirelesslan_list',
+ url_params={'group_id': 'pk'},
+ verbose_name='Wireless LANs'
+ )
+ actions = ButtonsColumn(WirelessLANGroup)
+
+ class Meta(BaseTable.Meta):
+ model = WirelessLANGroup
+ fields = ('pk', 'name', 'wirelesslan_count', 'description', 'slug', 'actions')
+ default_columns = ('pk', 'name', 'wirelesslan_count', 'description', 'actions')
+
+
class WirelessLANTable(BaseTable):
pk = ToggleColumn()
ssid = tables.Column(
diff --git a/netbox/wireless/urls.py b/netbox/wireless/urls.py
index 21d704e6a..684f55ad5 100644
--- a/netbox/wireless/urls.py
+++ b/netbox/wireless/urls.py
@@ -7,6 +7,17 @@ from .models import *
app_name = 'wireless'
urlpatterns = (
+ # Wireless LAN groups
+ path('wireless-lan-groups/', views.WirelessLANGroupListView.as_view(), name='wirelesslangroup_list'),
+ path('wireless-lan-groups/add/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_add'),
+ path('wireless-lan-groups/import/', views.WirelessLANGroupBulkImportView.as_view(), name='wirelesslangroup_import'),
+ path('wireless-lan-groups/edit/', views.WirelessLANGroupBulkEditView.as_view(), name='wirelesslangroup_bulk_edit'),
+ path('wireless-lan-groups/delete/', views.WirelessLANGroupBulkDeleteView.as_view(), name='wirelesslangroup_bulk_delete'),
+ path('wireless-lan-groups//', views.WirelessLANGroupView.as_view(), name='wirelesslangroup'),
+ path('wireless-lan-groups//edit/', views.WirelessLANGroupEditView.as_view(), name='wirelesslangroup_edit'),
+ path('wireless-lan-groups//delete/', views.WirelessLANGroupDeleteView.as_view(), name='wirelesslangroup_delete'),
+ path('wireless-lan-groups//changelog/', ObjectChangeLogView.as_view(), name='wirelesslangroup_changelog', kwargs={'model': WirelessLANGroup}),
+
# Wireless LANs
path('wireless-lans/', views.WirelessLANListView.as_view(), name='wirelesslan_list'),
path('wireless-lans/add/', views.WirelessLANEditView.as_view(), name='wirelesslan_add'),
diff --git a/netbox/wireless/views.py b/netbox/wireless/views.py
index 041ffbd42..6405d46df 100644
--- a/netbox/wireless/views.py
+++ b/netbox/wireless/views.py
@@ -1,8 +1,81 @@
from netbox.views import generic
+from utilities.tables import paginate_table
from . import filtersets, forms, tables
from .models import *
+#
+# Wireless LAN groups
+#
+
+class WirelessLANGroupListView(generic.ObjectListView):
+ queryset = WirelessLANGroup.objects.add_related_count(
+ WirelessLANGroup.objects.all(),
+ WirelessLAN,
+ 'group',
+ 'wirelesslan_count',
+ cumulative=True
+ )
+ filterset = filtersets.WirelessLANGroupFilterSet
+ filterset_form = forms.WirelessLANGroupFilterForm
+ table = tables.WirelessLANGroupTable
+
+
+class WirelessLANGroupView(generic.ObjectView):
+ queryset = WirelessLANGroup.objects.all()
+
+ def get_extra_context(self, request, instance):
+ wirelesslans = WirelessLAN.objects.restrict(request.user, 'view').filter(
+ group=instance
+ )
+ wirelesslans_table = tables.WirelessLANTable(wirelesslans, exclude=('group',))
+ paginate_table(wirelesslans_table, request)
+
+ return {
+ 'wirelesslans_table': wirelesslans_table,
+ }
+
+
+class WirelessLANGroupEditView(generic.ObjectEditView):
+ queryset = WirelessLANGroup.objects.all()
+ model_form = forms.WirelessLANGroupForm
+
+
+class WirelessLANGroupDeleteView(generic.ObjectDeleteView):
+ queryset = WirelessLANGroup.objects.all()
+
+
+class WirelessLANGroupBulkImportView(generic.BulkImportView):
+ queryset = WirelessLANGroup.objects.all()
+ model_form = forms.WirelessLANGroupCSVForm
+ table = tables.WirelessLANGroupTable
+
+
+class WirelessLANGroupBulkEditView(generic.BulkEditView):
+ queryset = WirelessLANGroup.objects.add_related_count(
+ WirelessLANGroup.objects.all(),
+ WirelessLAN,
+ 'group',
+ 'wirelesslan_count',
+ cumulative=True
+ )
+ filterset = filtersets.WirelessLANGroupFilterSet
+ table = tables.WirelessLANGroupTable
+ form = forms.WirelessLANGroupBulkEditForm
+
+
+class WirelessLANGroupBulkDeleteView(generic.BulkDeleteView):
+ queryset = WirelessLANGroup.objects.add_related_count(
+ WirelessLANGroup.objects.all(),
+ WirelessLAN,
+ 'group',
+ 'wirelesslan_count',
+ cumulative=True
+ )
+ filterset = filtersets.WirelessLANGroupFilterSet
+ table = tables.WirelessLANGroupTable
+
+
#
# Wireless LANs
#