diff --git a/netbox/templates/users/group.html b/netbox/templates/users/group.html
index d3f02af12..defc61854 100644
--- a/netbox/templates/users/group.html
+++ b/netbox/templates/users/group.html
@@ -17,6 +17,10 @@
{% trans "Name" %} |
{{ object.name }} |
+
+ {% trans "Description" %} |
+ {{ object.description|placeholder }} |
+
diff --git a/netbox/users/api/serializers_/users.py b/netbox/users/api/serializers_/users.py
index ded5dd1d9..b546aeb7b 100644
--- a/netbox/users/api/serializers_/users.py
+++ b/netbox/users/api/serializers_/users.py
@@ -29,7 +29,7 @@ class GroupSerializer(ValidatedModelSerializer):
class Meta:
model = Group
fields = ('id', 'url', 'display', 'name', 'permissions', 'user_count')
- brief_fields = ('id', 'url', 'display', 'name')
+ brief_fields = ('id', 'url', 'display', 'name', 'description')
class UserSerializer(ValidatedModelSerializer):
diff --git a/netbox/users/filtersets.py b/netbox/users/filtersets.py
index da0095a1c..14c02ed37 100644
--- a/netbox/users/filtersets.py
+++ b/netbox/users/filtersets.py
@@ -40,7 +40,10 @@ class GroupFilterSet(BaseFilterSet):
def search(self, queryset, name, value):
if not value.strip():
return queryset
- return queryset.filter(name__icontains=value)
+ return queryset.filter(
+ Q(name__icontains=value) |
+ Q(description__icontains=value)
+ )
class UserFilterSet(BaseFilterSet):
diff --git a/netbox/users/forms/bulk_edit.py b/netbox/users/forms/bulk_edit.py
index a26842d09..52a022de3 100644
--- a/netbox/users/forms/bulk_edit.py
+++ b/netbox/users/forms/bulk_edit.py
@@ -10,6 +10,7 @@ from utilities.forms.rendering import FieldSet
from utilities.forms.widgets import BulkEditNullBooleanSelect, DateTimePicker
__all__ = (
+ 'GroupBulkEditForm',
'ObjectPermissionBulkEditForm',
'UserBulkEditForm',
'TokenBulkEditForm',
@@ -54,6 +55,24 @@ class UserBulkEditForm(forms.Form):
nullable_fields = ('first_name', 'last_name')
+class GroupBulkEditForm(forms.Form):
+ pk = forms.ModelMultipleChoiceField(
+ queryset=Group.objects.all(),
+ widget=forms.MultipleHiddenInput
+ )
+ description = forms.CharField(
+ label=_('Description'),
+ max_length=200,
+ required=False
+ )
+
+ model = User
+ fieldsets = (
+ FieldSet('description'),
+ )
+ nullable_fields = ('description',)
+
+
class ObjectPermissionBulkEditForm(forms.Form):
pk = forms.ModelMultipleChoiceField(
queryset=ObjectPermission.objects.all(),
diff --git a/netbox/users/forms/bulk_import.py b/netbox/users/forms/bulk_import.py
index cbaa1ad76..748338a64 100644
--- a/netbox/users/forms/bulk_import.py
+++ b/netbox/users/forms/bulk_import.py
@@ -15,9 +15,7 @@ class GroupImportForm(CSVModelForm):
class Meta:
model = Group
- fields = (
- 'name',
- )
+ fields = ('name', 'description')
class UserImportForm(CSVModelForm):
diff --git a/netbox/users/forms/model_forms.py b/netbox/users/forms/model_forms.py
index 2d4431f96..7320e603b 100644
--- a/netbox/users/forms/model_forms.py
+++ b/netbox/users/forms/model_forms.py
@@ -19,12 +19,12 @@ from utilities.forms.widgets import DateTimePicker
from utilities.permissions import qs_filter_from_constraints
__all__ = (
- 'UserTokenForm',
'GroupForm',
'ObjectPermissionForm',
'TokenForm',
'UserConfigForm',
'UserForm',
+ 'UserTokenForm',
'TokenForm',
)
@@ -237,7 +237,7 @@ class GroupForm(forms.ModelForm):
)
fieldsets = (
- FieldSet('name'),
+ FieldSet('name', 'description'),
FieldSet('users', name=_('Users')),
FieldSet('object_permissions', name=_('Permissions')),
)
@@ -245,7 +245,7 @@ class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = [
- 'name', 'users', 'object_permissions',
+ 'name', 'description', 'users', 'object_permissions',
]
def __init__(self, *args, **kwargs):
diff --git a/netbox/users/tables.py b/netbox/users/tables.py
index 813d729c9..fc2f9702d 100644
--- a/netbox/users/tables.py
+++ b/netbox/users/tables.py
@@ -68,10 +68,7 @@ class GroupTable(NetBoxTable):
class Meta(NetBoxTable.Meta):
model = Group
- fields = (
- 'pk', 'id', 'name', 'users_count',
- )
- default_columns = ('pk', 'name', 'users_count', )
+ fields = ('pk', 'id', 'name', 'users_count', 'description')
class ObjectPermissionTable(NetBoxTable):
diff --git a/netbox/users/tests/test_views.py b/netbox/users/tests/test_views.py
index 588730dbd..8711e2b44 100644
--- a/netbox/users/tests/test_views.py
+++ b/netbox/users/tests/test_views.py
@@ -66,6 +66,7 @@ class GroupTestCase(
ViewTestCases.DeleteObjectViewTestCase,
ViewTestCases.ListObjectsViewTestCase,
ViewTestCases.BulkImportObjectsViewTestCase,
+ ViewTestCases.BulkEditObjectsViewTestCase,
ViewTestCases.BulkDeleteObjectsViewTestCase,
):
model = Group
@@ -99,6 +100,10 @@ class GroupTestCase(
f"{groups[2].pk},group9",
)
+ cls.bulk_edit_data = {
+ 'description': 'New description',
+ }
+
class ObjectPermissionTestCase(
ViewTestCases.GetObjectViewTestCase,
diff --git a/netbox/users/urls.py b/netbox/users/urls.py
index adfeba378..0540eae1f 100644
--- a/netbox/users/urls.py
+++ b/netbox/users/urls.py
@@ -25,6 +25,7 @@ urlpatterns = [
# Groups
path('groups/', views.GroupListView.as_view(), name='group_list'),
path('groups/add/', views.GroupEditView.as_view(), name='group_add'),
+ path('groups/edit/', views.GroupBulkEditView.as_view(), name='group_bulk_edit'),
path('groups/import/', views.GroupBulkImportView.as_view(), name='group_import'),
path('groups/delete/', views.GroupBulkDeleteView.as_view(), name='group_bulk_delete'),
path('groups//', include(get_model_urls('users', 'group'))),
diff --git a/netbox/users/views.py b/netbox/users/views.py
index a8754d4d9..624c8c8b4 100644
--- a/netbox/users/views.py
+++ b/netbox/users/views.py
@@ -138,6 +138,13 @@ class GroupBulkImportView(generic.BulkImportView):
model_form = forms.GroupImportForm
+class GroupBulkEditView(generic.BulkEditView):
+ queryset = Group.objects.all()
+ filterset = filtersets.GroupFilterSet
+ table = tables.GroupTable
+ form = forms.GroupBulkEditForm
+
+
class GroupBulkDeleteView(generic.BulkDeleteView):
queryset = Group.objects.annotate(users_count=Count('user')).order_by('name')
filterset = filtersets.GroupFilterSet