diff --git a/netbox/dcim/api/serializers_/sites.py b/netbox/dcim/api/serializers_/sites.py
index b818cd954..90f7b5d35 100644
--- a/netbox/dcim/api/serializers_/sites.py
+++ b/netbox/dcim/api/serializers_/sites.py
@@ -27,7 +27,7 @@ class RegionSerializer(NestedGroupModelSerializer):
model = Region
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'site_count', 'prefix_count', '_depth',
+ 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
@@ -41,7 +41,7 @@ class SiteGroupSerializer(NestedGroupModelSerializer):
model = SiteGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'site_count', 'prefix_count', '_depth',
+ 'created', 'last_updated', 'site_count', 'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'site_count', '_depth')
@@ -93,6 +93,6 @@ class LocationSerializer(NestedGroupModelSerializer):
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count',
- 'prefix_count', '_depth',
+ 'prefix_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')
diff --git a/netbox/dcim/filtersets.py b/netbox/dcim/filtersets.py
index e46730da8..6f9f481c3 100644
--- a/netbox/dcim/filtersets.py
+++ b/netbox/dcim/filtersets.py
@@ -11,7 +11,8 @@ from ipam.filtersets import PrimaryIPFilterSet
from ipam.models import ASN, IPAddress, VLANTranslationPolicy, VRF
from netbox.choices import ColorChoices
from netbox.filtersets import (
- BaseFilterSet, ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet,
+ BaseFilterSet, ChangeLoggedModelFilterSet, NestedGroupModelFilterSet, NetBoxModelFilterSet,
+ OrganizationalModelFilterSet,
)
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
from tenancy.models import *
@@ -81,7 +82,7 @@ __all__ = (
)
-class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
+class RegionFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=Region.objects.all(),
label=_('Parent region (ID)'),
@@ -111,7 +112,7 @@ class RegionFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
fields = ('id', 'name', 'slug', 'description')
-class SiteGroupFilterSet(OrganizationalModelFilterSet, ContactModelFilterSet):
+class SiteGroupFilterSet(NestedGroupModelFilterSet, ContactModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=SiteGroup.objects.all(),
label=_('Parent site group (ID)'),
@@ -205,7 +206,7 @@ class SiteFilterSet(NetBoxModelFilterSet, TenancyFilterSet, ContactModelFilterSe
return queryset.filter(qs_filter).distinct()
-class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalModelFilterSet):
+class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, NestedGroupModelFilterSet):
region_id = TreeNodeMultipleChoiceFilter(
queryset=Region.objects.all(),
field_name='site__region',
@@ -275,13 +276,13 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM
fields = ('id', 'name', 'slug', 'facility', 'description')
def search(self, queryset, name, value):
- if not value.strip():
- return queryset
- return queryset.filter(
- Q(name__icontains=value) |
- Q(facility__icontains=value) |
- Q(description__icontains=value)
- )
+ # extended in order to include querying on Location.facility
+ queryset = super().search(queryset, name, value)
+
+ if value.strip():
+ queryset = queryset | queryset.model.objects.filter(facility__icontains=value)
+
+ return queryset
class RackRoleFilterSet(OrganizationalModelFilterSet):
diff --git a/netbox/dcim/forms/bulk_edit.py b/netbox/dcim/forms/bulk_edit.py
index 3b9a183cd..c1da9c8d1 100644
--- a/netbox/dcim/forms/bulk_edit.py
+++ b/netbox/dcim/forms/bulk_edit.py
@@ -78,12 +78,13 @@ class RegionBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = Region
fieldsets = (
FieldSet('parent', 'description'),
)
- nullable_fields = ('parent', 'description')
+ nullable_fields = ('parent', 'description', 'comments')
class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
@@ -97,12 +98,13 @@ class SiteGroupBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = SiteGroup
fieldsets = (
FieldSet('parent', 'description'),
)
- nullable_fields = ('parent', 'description')
+ nullable_fields = ('parent', 'description', 'comments')
class SiteBulkEditForm(NetBoxModelBulkEditForm):
@@ -197,12 +199,13 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = Location
fieldsets = (
FieldSet('site', 'parent', 'status', 'tenant', 'description'),
)
- nullable_fields = ('parent', 'tenant', 'description')
+ nullable_fields = ('parent', 'tenant', 'description', 'comments')
class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
diff --git a/netbox/dcim/forms/bulk_import.py b/netbox/dcim/forms/bulk_import.py
index 92f7220da..469e40217 100644
--- a/netbox/dcim/forms/bulk_import.py
+++ b/netbox/dcim/forms/bulk_import.py
@@ -68,7 +68,7 @@ class RegionImportForm(NetBoxModelImportForm):
class Meta:
model = Region
- fields = ('name', 'slug', 'parent', 'description', 'tags')
+ fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class SiteGroupImportForm(NetBoxModelImportForm):
@@ -82,7 +82,7 @@ class SiteGroupImportForm(NetBoxModelImportForm):
class Meta:
model = SiteGroup
- fields = ('name', 'slug', 'parent', 'description')
+ fields = ('name', 'slug', 'parent', 'description', 'comments', 'tags')
class SiteImportForm(NetBoxModelImportForm):
@@ -160,7 +160,10 @@ class LocationImportForm(NetBoxModelImportForm):
class Meta:
model = Location
- fields = ('site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description', 'tags')
+ fields = (
+ 'site', 'parent', 'name', 'slug', 'status', 'tenant', 'facility', 'description',
+ 'tags', 'comments',
+ )
def __init__(self, data=None, *args, **kwargs):
super().__init__(data, *args, **kwargs)
diff --git a/netbox/dcim/forms/model_forms.py b/netbox/dcim/forms/model_forms.py
index 91e23e8b1..dea031b64 100644
--- a/netbox/dcim/forms/model_forms.py
+++ b/netbox/dcim/forms/model_forms.py
@@ -78,6 +78,7 @@ class RegionForm(NetBoxModelForm):
required=False
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -86,7 +87,7 @@ class RegionForm(NetBoxModelForm):
class Meta:
model = Region
fields = (
- 'parent', 'name', 'slug', 'description', 'tags',
+ 'parent', 'name', 'slug', 'description', 'tags', 'comments',
)
@@ -97,6 +98,7 @@ class SiteGroupForm(NetBoxModelForm):
required=False
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags'),
@@ -105,7 +107,7 @@ class SiteGroupForm(NetBoxModelForm):
class Meta:
model = SiteGroup
fields = (
- 'parent', 'name', 'slug', 'description', 'tags',
+ 'parent', 'name', 'slug', 'description', 'comments', 'tags',
)
@@ -179,6 +181,7 @@ class LocationForm(TenancyForm, NetBoxModelForm):
}
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
@@ -188,7 +191,8 @@ class LocationForm(TenancyForm, NetBoxModelForm):
class Meta:
model = Location
fields = (
- 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'tags',
+ 'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
+ 'facility', 'tags', 'comments',
)
diff --git a/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py b/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py
new file mode 100644
index 000000000..ffdc5ba8a
--- /dev/null
+++ b/netbox/dcim/migrations/0202_location_comments_region_comments_sitegroup_comments.py
@@ -0,0 +1,26 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('dcim', '0201_add_power_outlet_status'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='location',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AddField(
+ model_name='region',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AddField(
+ model_name='sitegroup',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ ]
diff --git a/netbox/dcim/search.py b/netbox/dcim/search.py
index 5dea2a09b..a85005679 100644
--- a/netbox/dcim/search.py
+++ b/netbox/dcim/search.py
@@ -144,6 +144,7 @@ class LocationIndex(SearchIndex):
('facility', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('site', 'status', 'tenant', 'facility', 'description')
@@ -317,6 +318,7 @@ class RegionIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('parent', 'description')
@@ -343,6 +345,7 @@ class SiteGroupIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('parent', 'description')
diff --git a/netbox/dcim/tables/sites.py b/netbox/dcim/tables/sites.py
index 77844f086..7d2f0e0cc 100644
--- a/netbox/dcim/tables/sites.py
+++ b/netbox/dcim/tables/sites.py
@@ -32,12 +32,15 @@ class RegionTable(ContactsColumnMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='dcim:region_list'
)
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
class Meta(NetBoxTable.Meta):
model = Region
fields = (
- 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
- 'actions',
+ 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
+ 'created', 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'site_count', 'description')
@@ -59,12 +62,15 @@ class SiteGroupTable(ContactsColumnMixin, NetBoxTable):
tags = columns.TagColumn(
url_name='dcim:sitegroup_list'
)
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
class Meta(NetBoxTable.Meta):
model = SiteGroup
fields = (
- 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'contacts', 'tags', 'created', 'last_updated',
- 'actions',
+ 'pk', 'id', 'name', 'slug', 'site_count', 'description', 'comments', 'contacts', 'tags',
+ 'created', 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'site_count', 'description')
@@ -153,12 +159,15 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
actions = columns.ActionsColumn(
extra_buttons=LOCATION_BUTTONS
)
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
class Meta(NetBoxTable.Meta):
model = Location
fields = (
'pk', 'id', 'name', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count', 'device_count',
- 'description', 'slug', 'contacts', 'tags', 'actions', 'created', 'last_updated',
+ 'description', 'slug', 'comments', 'contacts', 'tags', 'actions', 'created', 'last_updated',
)
default_columns = (
'pk', 'name', 'site', 'status', 'facility', 'tenant', 'rack_count', 'device_count', 'description'
diff --git a/netbox/dcim/tests/test_api.py b/netbox/dcim/tests/test_api.py
index 08f93f6ea..807ac77d4 100644
--- a/netbox/dcim/tests/test_api.py
+++ b/netbox/dcim/tests/test_api.py
@@ -74,6 +74,7 @@ class RegionTest(APIViewTestCases.APIViewTestCase):
{
'name': 'Region 4',
'slug': 'region-4',
+ 'comments': 'this is region 4, not region 5',
},
{
'name': 'Region 5',
@@ -86,13 +87,14 @@ class RegionTest(APIViewTestCases.APIViewTestCase):
]
bulk_update_data = {
'description': 'New description',
+ 'comments': 'New comments',
}
@classmethod
def setUpTestData(cls):
Region.objects.create(name='Region 1', slug='region-1')
- Region.objects.create(name='Region 2', slug='region-2')
+ Region.objects.create(name='Region 2', slug='region-2', comments='what in the world is happening?')
Region.objects.create(name='Region 3', slug='region-3')
@@ -103,26 +105,30 @@ class SiteGroupTest(APIViewTestCases.APIViewTestCase):
{
'name': 'Site Group 4',
'slug': 'site-group-4',
+ 'comments': '',
},
{
'name': 'Site Group 5',
'slug': 'site-group-5',
+ 'comments': 'not actually empty',
},
{
'name': 'Site Group 6',
'slug': 'site-group-6',
+ 'comments': 'Do I really exist?',
},
]
bulk_update_data = {
'description': 'New description',
+ 'comments': 'I do exist!',
}
@classmethod
def setUpTestData(cls):
SiteGroup.objects.create(name='Site Group 1', slug='site-group-1')
- SiteGroup.objects.create(name='Site Group 2', slug='site-group-2')
- SiteGroup.objects.create(name='Site Group 3', slug='site-group-3')
+ SiteGroup.objects.create(name='Site Group 2', slug='site-group-2', comments='')
+ SiteGroup.objects.create(name='Site Group 3', slug='site-group-3', comments='Hi!')
class SiteTest(APIViewTestCases.APIViewTestCase):
@@ -212,12 +218,14 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
name='Parent Location 1',
slug='parent-location-1',
status=LocationStatusChoices.STATUS_ACTIVE,
+ comments='First!'
),
Location.objects.create(
site=sites[1],
name='Parent Location 2',
slug='parent-location-2',
status=LocationStatusChoices.STATUS_ACTIVE,
+ comments='Second!'
),
)
@@ -227,6 +235,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
slug='location-1',
parent=parent_locations[0],
status=LocationStatusChoices.STATUS_ACTIVE,
+ comments='Third!'
)
Location.objects.create(
site=sites[0],
@@ -250,6 +259,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
'site': sites[1].pk,
'parent': parent_locations[1].pk,
'status': LocationStatusChoices.STATUS_PLANNED,
+ 'comments': '',
},
{
'name': 'Test Location 5',
@@ -257,6 +267,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
'site': sites[1].pk,
'parent': parent_locations[1].pk,
'status': LocationStatusChoices.STATUS_PLANNED,
+ 'comments': 'Somebody should check on this location',
},
{
'name': 'Test Location 6',
diff --git a/netbox/dcim/tests/test_filtersets.py b/netbox/dcim/tests/test_filtersets.py
index 7c9b8adc6..0c4bbbaff 100644
--- a/netbox/dcim/tests/test_filtersets.py
+++ b/netbox/dcim/tests/test_filtersets.py
@@ -67,9 +67,15 @@ class RegionTestCase(TestCase, ChangeLoggedFilterSetTests):
def setUpTestData(cls):
parent_regions = (
- Region(name='Region 1', slug='region-1', description='foobar1'),
- Region(name='Region 2', slug='region-2', description='foobar2'),
- Region(name='Region 3', slug='region-3', description='foobar3'),
+ Region(
+ name='Region 1', slug='region-1', description='foobar1', comments="There's nothing that",
+ ),
+ Region(
+ name='Region 2', slug='region-2', description='foobar2', comments='a hundred men or more',
+ ),
+ Region(
+ name='Region 3', slug='region-3', description='foobar3', comments='could ever do'
+ ),
)
for region in parent_regions:
region.save()
@@ -100,6 +106,13 @@ class RegionTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'there'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ params = {'q': 'hundred men could'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 0)
+
def test_name(self):
params = {'name': ['Region 1', 'Region 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@@ -148,13 +161,17 @@ class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
SiteGroup(name='Site Group 2A', slug='site-group-2a', parent=parent_groups[1]),
SiteGroup(name='Site Group 2B', slug='site-group-2b', parent=parent_groups[1]),
SiteGroup(name='Site Group 3A', slug='site-group-3a', parent=parent_groups[2]),
- SiteGroup(name='Site Group 3B', slug='site-group-3b', parent=parent_groups[2]),
+ SiteGroup(
+ name='Site Group 3B', slug='site-group-3b', parent=parent_groups[2], comments='this is a parent group',
+ ),
)
for site_group in groups:
site_group.save()
child_groups = (
- SiteGroup(name='Site Group 1A1', slug='site-group-1a1', parent=groups[0]),
+ SiteGroup(
+ name='Site Group 1A1', slug='site-group-1a1', parent=groups[0], comments='this is a child group',
+ ),
SiteGroup(name='Site Group 1B1', slug='site-group-1b1', parent=groups[1]),
SiteGroup(name='Site Group 2A1', slug='site-group-2a1', parent=groups[2]),
SiteGroup(name='Site Group 2B1', slug='site-group-2b1', parent=groups[3]),
@@ -168,6 +185,13 @@ class SiteGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'this'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ params = {'q': 'child'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
def test_name(self):
params = {'name': ['Site Group 1', 'Site Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@@ -401,6 +425,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
status=LocationStatusChoices.STATUS_PLANNED,
facility='Facility 1',
description='foobar1',
+ comments='',
),
Location(
name='Location 2A',
@@ -410,6 +435,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
status=LocationStatusChoices.STATUS_STAGING,
facility='Facility 2',
description='foobar2',
+ comments='First comment!',
),
Location(
name='Location 3A',
@@ -419,6 +445,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
status=LocationStatusChoices.STATUS_DECOMMISSIONING,
facility='Facility 3',
description='foobar3',
+ comments='_This_ is a **bold comment**',
),
)
for location in locations:
@@ -436,6 +463,13 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'this'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ params = {'q': 'comment'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
def test_name(self):
params = {'name': ['Location 1', 'Location 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
diff --git a/netbox/dcim/tests/test_views.py b/netbox/dcim/tests/test_views.py
index 4dea94c7d..83effa188 100644
--- a/netbox/dcim/tests/test_views.py
+++ b/netbox/dcim/tests/test_views.py
@@ -25,8 +25,10 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
# Create three Regions
regions = (
- Region(name='Region 1', slug='region-1'),
- Region(name='Region 2', slug='region-2'),
+ Region(name='Region 1', slug='region-1', comments=''),
+ Region(
+ name='Region 2', slug='region-2', comments="It's going to take a lot to drag me away from you"
+ ),
Region(name='Region 3', slug='region-3'),
)
for region in regions:
@@ -40,13 +42,14 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'parent': regions[2].pk,
'description': 'A new region',
'tags': [t.pk for t in tags],
+ 'comments': 'This comment is really exciting!',
}
cls.csv_data = (
- "name,slug,description",
- "Region 4,region-4,Fourth region",
- "Region 5,region-5,Fifth region",
- "Region 6,region-6,Sixth region",
+ "name,slug,description,comments",
+ "Region 4,region-4,Fourth region,",
+ "Region 5,region-5,Fifth region,hi guys",
+ "Region 6,region-6,Sixth region,bye guys",
)
cls.csv_update_data = (
@@ -58,6 +61,7 @@ class RegionTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'This comment is super exciting!!!',
}
@@ -69,7 +73,7 @@ class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
# Create three SiteGroups
sitegroups = (
- SiteGroup(name='Site Group 1', slug='site-group-1'),
+ SiteGroup(name='Site Group 1', slug='site-group-1', comments='Still here'),
SiteGroup(name='Site Group 2', slug='site-group-2'),
SiteGroup(name='Site Group 3', slug='site-group-3'),
)
@@ -84,24 +88,26 @@ class SiteGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'parent': sitegroups[2].pk,
'description': 'A new site group',
'tags': [t.pk for t in tags],
+ 'comments': 'still here',
}
cls.csv_data = (
- "name,slug,description",
- "Site Group 4,site-group-4,Fourth site group",
- "Site Group 5,site-group-5,Fifth site group",
- "Site Group 6,site-group-6,Sixth site group",
+ "name,slug,description,comments",
+ "Site Group 4,site-group-4,Fourth site group,",
+ "Site Group 5,site-group-5,Fifth site group,still hear",
+ "Site Group 6,site-group-6,Sixth site group,"
)
cls.csv_update_data = (
- "id,name,description",
- f"{sitegroups[0].pk},Site Group 7,Fourth site group7",
- f"{sitegroups[1].pk},Site Group 8,Fifth site group8",
- f"{sitegroups[2].pk},Site Group 0,Sixth site group9",
+ "id,name,description,comments",
+ f"{sitegroups[0].pk},Site Group 7,Fourth site group7,",
+ f"{sitegroups[1].pk},Site Group 8,Fifth site group8,when will it end",
+ f"{sitegroups[2].pk},Site Group 0,Sixth site group9,",
)
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'the end',
}
@@ -202,6 +208,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
site=site,
status=LocationStatusChoices.STATUS_ACTIVE,
tenant=tenant,
+ comments='',
),
Location(
name='Location 2',
@@ -209,6 +216,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
site=site,
status=LocationStatusChoices.STATUS_ACTIVE,
tenant=tenant,
+ comments='First comment!',
),
Location(
name='Location 3',
@@ -216,6 +224,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
site=site,
status=LocationStatusChoices.STATUS_ACTIVE,
tenant=tenant,
+ comments='_This_ is a **bold comment**',
),
)
for location in locations:
@@ -232,24 +241,26 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'tenant': tenant.pk,
'description': 'A new location',
'tags': [t.pk for t in tags],
+ 'comments': 'This comment is really boring',
}
cls.csv_data = (
- "site,tenant,name,slug,status,description",
- "Site 1,Tenant 1,Location 4,location-4,planned,Fourth location",
- "Site 1,Tenant 1,Location 5,location-5,planned,Fifth location",
- "Site 1,Tenant 1,Location 6,location-6,planned,Sixth location",
+ "site,tenant,name,slug,status,description,comments",
+ "Site 1,Tenant 1,Location 4,location-4,planned,Fourth location,",
+ "Site 1,Tenant 1,Location 5,location-5,planned,Fifth location,",
+ "Site 1,Tenant 1,Location 6,location-6,planned,Sixth location,hi!",
)
cls.csv_update_data = (
- "id,name,description",
- f"{locations[0].pk},Location 7,Fourth location7",
- f"{locations[1].pk},Location 8,Fifth location8",
- f"{locations[2].pk},Location 0,Sixth location9",
+ "id,name,description,comments",
+ f"{locations[0].pk},Location 7,Fourth location7,Useful comment",
+ f"{locations[1].pk},Location 8,Fifth location8,unuseful comment",
+ f"{locations[2].pk},Location 0,Sixth location9,",
)
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'This comment is also really boring',
}
diff --git a/netbox/netbox/filtersets.py b/netbox/netbox/filtersets.py
index b8fbe7ad5..d80b07e90 100644
--- a/netbox/netbox/filtersets.py
+++ b/netbox/netbox/filtersets.py
@@ -329,3 +329,19 @@ class OrganizationalModelFilterSet(NetBoxModelFilterSet):
models.Q(slug__icontains=value) |
models.Q(description__icontains=value)
)
+
+
+class NestedGroupModelFilterSet(NetBoxModelFilterSet):
+ """
+ A base FilterSet for models that inherit from NestedGroupModel
+ """
+ def search(self, queryset, name, value):
+ if value.strip():
+ queryset = queryset.filter(
+ models.Q(name__icontains=value) |
+ models.Q(slug__icontains=value) |
+ models.Q(description__icontains=value) |
+ models.Q(comments__icontains=value)
+ )
+
+ return queryset
diff --git a/netbox/netbox/models/__init__.py b/netbox/netbox/models/__init__.py
index b1f7cfd48..3ad0ac556 100644
--- a/netbox/netbox/models/__init__.py
+++ b/netbox/netbox/models/__init__.py
@@ -150,6 +150,10 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
max_length=200,
blank=True
)
+ comments = models.TextField(
+ verbose_name=_('comments'),
+ blank=True
+ )
objects = TreeManager()
diff --git a/netbox/templates/dcim/location.html b/netbox/templates/dcim/location.html
index 97dcc20f0..02e02a1ed 100644
--- a/netbox/templates/dcim/location.html
+++ b/netbox/templates/dcim/location.html
@@ -62,6 +62,7 @@
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/dcim/region.html b/netbox/templates/dcim/region.html
index 1e1b75cd5..c6acbb9ea 100644
--- a/netbox/templates/dcim/region.html
+++ b/netbox/templates/dcim/region.html
@@ -41,6 +41,7 @@
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/dcim/sitegroup.html b/netbox/templates/dcim/sitegroup.html
index 3ae43f210..9beb7c505 100644
--- a/netbox/templates/dcim/sitegroup.html
+++ b/netbox/templates/dcim/sitegroup.html
@@ -41,6 +41,7 @@
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/custom_fields.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/tenancy/contactgroup.html b/netbox/templates/tenancy/contactgroup.html
index bf6928c15..25b1da440 100644
--- a/netbox/templates/tenancy/contactgroup.html
+++ b/netbox/templates/tenancy/contactgroup.html
@@ -32,6 +32,7 @@
{% include 'inc/panels/tags.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/tenancy/tenantgroup.html b/netbox/templates/tenancy/tenantgroup.html
index 0567f2ab3..ecf95a024 100644
--- a/netbox/templates/tenancy/tenantgroup.html
+++ b/netbox/templates/tenancy/tenantgroup.html
@@ -40,6 +40,7 @@
{% include 'inc/panels/tags.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/templates/wireless/wirelesslangroup.html b/netbox/templates/wireless/wirelesslangroup.html
index cb08b1b52..913e9da4c 100644
--- a/netbox/templates/wireless/wirelesslangroup.html
+++ b/netbox/templates/wireless/wirelesslangroup.html
@@ -40,6 +40,7 @@
{% include 'inc/panels/tags.html' %}
+ {% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
diff --git a/netbox/tenancy/api/serializers_/contacts.py b/netbox/tenancy/api/serializers_/contacts.py
index a5c4dd741..7fc6a15e6 100644
--- a/netbox/tenancy/api/serializers_/contacts.py
+++ b/netbox/tenancy/api/serializers_/contacts.py
@@ -26,7 +26,7 @@ class ContactGroupSerializer(NestedGroupModelSerializer):
model = ContactGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'contact_count', '_depth',
+ 'created', 'last_updated', 'contact_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'contact_count', '_depth')
diff --git a/netbox/tenancy/api/serializers_/tenants.py b/netbox/tenancy/api/serializers_/tenants.py
index 54e906f1d..189397c70 100644
--- a/netbox/tenancy/api/serializers_/tenants.py
+++ b/netbox/tenancy/api/serializers_/tenants.py
@@ -19,7 +19,7 @@ class TenantGroupSerializer(NestedGroupModelSerializer):
model = TenantGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'tenant_count', '_depth',
+ 'created', 'last_updated', 'tenant_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'tenant_count', '_depth')
diff --git a/netbox/tenancy/filtersets.py b/netbox/tenancy/filtersets.py
index 0042ca427..ca0142db6 100644
--- a/netbox/tenancy/filtersets.py
+++ b/netbox/tenancy/filtersets.py
@@ -2,7 +2,7 @@ import django_filters
from django.db.models import Q
from django.utils.translation import gettext as _
-from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet
+from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet, OrganizationalModelFilterSet
from utilities.filters import ContentTypeFilter, TreeNodeMultipleChoiceFilter
from .models import *
@@ -22,7 +22,7 @@ __all__ = (
# Contacts
#
-class ContactGroupFilterSet(OrganizationalModelFilterSet):
+class ContactGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=ContactGroup.objects.all(),
label=_('Parent contact group (ID)'),
@@ -168,7 +168,7 @@ class ContactModelFilterSet(django_filters.FilterSet):
# Tenancy
#
-class TenantGroupFilterSet(OrganizationalModelFilterSet):
+class TenantGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=TenantGroup.objects.all(),
label=_('Parent tenant group (ID)'),
diff --git a/netbox/tenancy/forms/bulk_edit.py b/netbox/tenancy/forms/bulk_edit.py
index 63eaaad9d..aeed18e12 100644
--- a/netbox/tenancy/forms/bulk_edit.py
+++ b/netbox/tenancy/forms/bulk_edit.py
@@ -33,9 +33,10 @@ class TenantGroupBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = TenantGroup
- nullable_fields = ('parent', 'description')
+ nullable_fields = ('parent', 'description', 'comments')
class TenantBulkEditForm(NetBoxModelBulkEditForm):
@@ -67,12 +68,13 @@ class ContactGroupBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = ContactGroup
fieldsets = (
FieldSet('parent', 'description'),
)
- nullable_fields = ('parent', 'description')
+ nullable_fields = ('parent', 'description', 'comments')
class ContactRoleBulkEditForm(NetBoxModelBulkEditForm):
diff --git a/netbox/tenancy/forms/bulk_import.py b/netbox/tenancy/forms/bulk_import.py
index c4f48347a..8234513ae 100644
--- a/netbox/tenancy/forms/bulk_import.py
+++ b/netbox/tenancy/forms/bulk_import.py
@@ -31,7 +31,7 @@ class TenantGroupImportForm(NetBoxModelImportForm):
class Meta:
model = TenantGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags')
+ fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class TenantImportForm(NetBoxModelImportForm):
@@ -65,7 +65,7 @@ class ContactGroupImportForm(NetBoxModelImportForm):
class Meta:
model = ContactGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags')
+ fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class ContactRoleImportForm(NetBoxModelImportForm):
diff --git a/netbox/tenancy/forms/model_forms.py b/netbox/tenancy/forms/model_forms.py
index 65beebca9..6ef9d8560 100644
--- a/netbox/tenancy/forms/model_forms.py
+++ b/netbox/tenancy/forms/model_forms.py
@@ -27,6 +27,7 @@ class TenantGroupForm(NetBoxModelForm):
required=False
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Tenant Group')),
@@ -35,7 +36,7 @@ class TenantGroupForm(NetBoxModelForm):
class Meta:
model = TenantGroup
fields = [
- 'parent', 'name', 'slug', 'description', 'tags',
+ 'parent', 'name', 'slug', 'description', 'tags', 'comments'
]
@@ -70,6 +71,7 @@ class ContactGroupForm(NetBoxModelForm):
required=False
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Contact Group')),
@@ -77,7 +79,7 @@ class ContactGroupForm(NetBoxModelForm):
class Meta:
model = ContactGroup
- fields = ('parent', 'name', 'slug', 'description', 'tags')
+ fields = ('parent', 'name', 'slug', 'description', 'tags', 'comments')
class ContactRoleForm(NetBoxModelForm):
diff --git a/netbox/tenancy/migrations/0018_contactgroup_comments_tenantgroup_comments.py b/netbox/tenancy/migrations/0018_contactgroup_comments_tenantgroup_comments.py
new file mode 100644
index 000000000..5f6a95149
--- /dev/null
+++ b/netbox/tenancy/migrations/0018_contactgroup_comments_tenantgroup_comments.py
@@ -0,0 +1,21 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('tenancy', '0017_natural_ordering'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='contactgroup',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ migrations.AddField(
+ model_name='tenantgroup',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ ]
diff --git a/netbox/tenancy/search.py b/netbox/tenancy/search.py
index 56903d6b1..f9441c974 100644
--- a/netbox/tenancy/search.py
+++ b/netbox/tenancy/search.py
@@ -25,6 +25,7 @@ class ContactGroupIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('description',)
@@ -59,5 +60,6 @@ class TenantGroupIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('description',)
diff --git a/netbox/tenancy/tables/contacts.py b/netbox/tenancy/tables/contacts.py
index 07af8382e..e9579cbe5 100644
--- a/netbox/tenancy/tables/contacts.py
+++ b/netbox/tenancy/tables/contacts.py
@@ -27,11 +27,15 @@ class ContactGroupTable(NetBoxTable):
tags = columns.TagColumn(
url_name='tenancy:contactgroup_list'
)
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
class Meta(NetBoxTable.Meta):
model = ContactGroup
fields = (
- 'pk', 'name', 'contact_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+ 'pk', 'name', 'contact_count', 'description', 'comments', 'slug', 'tags', 'created',
+ 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'contact_count', 'description')
diff --git a/netbox/tenancy/tables/tenants.py b/netbox/tenancy/tables/tenants.py
index a10133a64..70f263dbe 100644
--- a/netbox/tenancy/tables/tenants.py
+++ b/netbox/tenancy/tables/tenants.py
@@ -24,11 +24,15 @@ class TenantGroupTable(NetBoxTable):
tags = columns.TagColumn(
url_name='tenancy:tenantgroup_list'
)
+ comments = columns.MarkdownColumn(
+ verbose_name=_('Comments'),
+ )
class Meta(NetBoxTable.Meta):
model = TenantGroup
fields = (
- 'pk', 'id', 'name', 'tenant_count', 'description', 'slug', 'tags', 'created', 'last_updated', 'actions',
+ 'pk', 'id', 'name', 'tenant_count', 'description', 'comments', 'slug', 'tags', 'created',
+ 'last_updated', 'actions',
)
default_columns = ('pk', 'name', 'tenant_count', 'description')
diff --git a/netbox/tenancy/tests/test_api.py b/netbox/tenancy/tests/test_api.py
index 3bacb8fea..55a54c91f 100644
--- a/netbox/tenancy/tests/test_api.py
+++ b/netbox/tenancy/tests/test_api.py
@@ -21,6 +21,7 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase):
brief_fields = ['_depth', 'description', 'display', 'id', 'name', 'slug', 'tenant_count', 'url']
bulk_update_data = {
'description': 'New description',
+ 'comments': 'New Comment',
}
@classmethod
@@ -28,12 +29,17 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase):
parent_tenant_groups = (
TenantGroup.objects.create(name='Parent Tenant Group 1', slug='parent-tenant-group-1'),
- TenantGroup.objects.create(name='Parent Tenant Group 2', slug='parent-tenant-group-2'),
+ TenantGroup.objects.create(
+ name='Parent Tenant Group 2', slug='parent-tenant-group-2', comments='Parent Group 2 comment',
+ ),
)
TenantGroup.objects.create(name='Tenant Group 1', slug='tenant-group-1', parent=parent_tenant_groups[0])
TenantGroup.objects.create(name='Tenant Group 2', slug='tenant-group-2', parent=parent_tenant_groups[0])
- TenantGroup.objects.create(name='Tenant Group 3', slug='tenant-group-3', parent=parent_tenant_groups[0])
+ TenantGroup.objects.create(
+ name='Tenant Group 3', slug='tenant-group-3', parent=parent_tenant_groups[0],
+ comments='Tenant Group 3 comment'
+ )
cls.create_data = [
{
@@ -50,6 +56,7 @@ class TenantGroupTest(APIViewTestCases.APIViewTestCase):
'name': 'Tenant Group 6',
'slug': 'tenant-group-6',
'parent': parent_tenant_groups[1].pk,
+ 'comments': 'Tenant Group 6 comment',
},
]
@@ -107,13 +114,18 @@ class ContactGroupTest(APIViewTestCases.APIViewTestCase):
def setUpTestData(cls):
parent_contact_groups = (
- ContactGroup.objects.create(name='Parent Contact Group 1', slug='parent-contact-group-1'),
+ ContactGroup.objects.create(
+ name='Parent Contact Group 1', slug='parent-contact-group-1', comments='Parent 1 comment'
+ ),
ContactGroup.objects.create(name='Parent Contact Group 2', slug='parent-contact-group-2'),
)
ContactGroup.objects.create(name='Contact Group 1', slug='contact-group-1', parent=parent_contact_groups[0])
ContactGroup.objects.create(name='Contact Group 2', slug='contact-group-2', parent=parent_contact_groups[0])
- ContactGroup.objects.create(name='Contact Group 3', slug='contact-group-3', parent=parent_contact_groups[0])
+ ContactGroup.objects.create(
+ name='Contact Group 3', slug='contact-group-3', parent=parent_contact_groups[0],
+ comments='Child Group 3 comment',
+ )
cls.create_data = [
{
@@ -125,11 +137,13 @@ class ContactGroupTest(APIViewTestCases.APIViewTestCase):
'name': 'Contact Group 5',
'slug': 'contact-group-5',
'parent': parent_contact_groups[1].pk,
+ 'comments': '',
},
{
'name': 'Contact Group 6',
'slug': 'contact-group-6',
'parent': parent_contact_groups[1].pk,
+ 'comments': 'Child Group 6 comment',
},
]
diff --git a/netbox/tenancy/tests/test_filtersets.py b/netbox/tenancy/tests/test_filtersets.py
index d44d78ec4..fcb354079 100644
--- a/netbox/tenancy/tests/test_filtersets.py
+++ b/netbox/tenancy/tests/test_filtersets.py
@@ -16,7 +16,7 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
parent_tenant_groups = (
TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
- TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
+ TenantGroup(name='Tenant Group 2', slug='tenant-group-2', comments='Parent group 2 comment'),
TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
)
for tenant_group in parent_tenant_groups:
@@ -27,7 +27,8 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
name='Tenant Group 1A',
slug='tenant-group-1a',
parent=parent_tenant_groups[0],
- description='foobar1'
+ description='foobar1',
+ comments='Tenant Group 1A comment',
),
TenantGroup(
name='Tenant Group 2A',
@@ -48,7 +49,10 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
child_tenant_groups = (
TenantGroup(name='Tenant Group 1A1', slug='tenant-group-1a1', parent=tenant_groups[0]),
TenantGroup(name='Tenant Group 2A1', slug='tenant-group-2a1', parent=tenant_groups[1]),
- TenantGroup(name='Tenant Group 3A1', slug='tenant-group-3a1', parent=tenant_groups[2]),
+ TenantGroup(
+ name='Tenant Group 3A1', slug='tenant-group-3a1', parent=tenant_groups[2],
+ comments='Tenant Group 3A1 comment',
+ ),
)
for tenant_group in child_tenant_groups:
tenant_group.save()
@@ -57,6 +61,13 @@ class TenantGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'parent'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ params = {'q': 'comment'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
def test_name(self):
params = {'name': ['Tenant Group 1', 'Tenant Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
@@ -139,7 +150,7 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
parent_contact_groups = (
ContactGroup(name='Contact Group 1', slug='contact-group-1'),
- ContactGroup(name='Contact Group 2', slug='contact-group-2'),
+ ContactGroup(name='Contact Group 2', slug='contact-group-2', comments='Parent group 2'),
ContactGroup(name='Contact Group 3', slug='contact-group-3'),
)
for contact_group in parent_contact_groups:
@@ -162,14 +173,18 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
name='Contact Group 3A',
slug='contact-group-3a',
parent=parent_contact_groups[2],
- description='foobar3'
+ description='foobar3',
+ comments='Contact Group 3A comment, not a parent',
),
)
for contact_group in contact_groups:
contact_group.save()
child_contact_groups = (
- ContactGroup(name='Contact Group 1A1', slug='contact-group-1a1', parent=contact_groups[0]),
+ ContactGroup(
+ name='Contact Group 1A1', slug='contact-group-1a1', parent=contact_groups[0],
+ comments='Contact Group 1A1 comment',
+ ),
ContactGroup(name='Contact Group 2A1', slug='contact-group-2a1', parent=contact_groups[1]),
ContactGroup(name='Contact Group 3A1', slug='contact-group-3a1', parent=contact_groups[2]),
)
@@ -180,6 +195,13 @@ class ContactGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'parent'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
+
+ params = {'q': '1A1'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
def test_name(self):
params = {'name': ['Contact Group 1', 'Contact Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
diff --git a/netbox/tenancy/tests/test_views.py b/netbox/tenancy/tests/test_views.py
index ec962e6e6..4d1a45a82 100644
--- a/netbox/tenancy/tests/test_views.py
+++ b/netbox/tenancy/tests/test_views.py
@@ -15,7 +15,7 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
tenant_groups = (
TenantGroup(name='Tenant Group 1', slug='tenant-group-1'),
- TenantGroup(name='Tenant Group 2', slug='tenant-group-2'),
+ TenantGroup(name='Tenant Group 2', slug='tenant-group-2', comments='Tenant Group 2 comment'),
TenantGroup(name='Tenant Group 3', slug='tenant-group-3'),
)
for tenanantgroup in tenant_groups:
@@ -28,24 +28,26 @@ class TenantGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'slug': 'tenant-group-x',
'description': 'A new tenant group',
'tags': [t.pk for t in tags],
+ 'comments': 'Tenant Group X comment',
}
cls.csv_data = (
- "name,slug,description",
- "Tenant Group 4,tenant-group-4,Fourth tenant group",
- "Tenant Group 5,tenant-group-5,Fifth tenant group",
- "Tenant Group 6,tenant-group-6,Sixth tenant group",
+ "name,slug,description,comments",
+ "Tenant Group 4,tenant-group-4,Fourth tenant group,",
+ "Tenant Group 5,tenant-group-5,Fifth tenant group,",
+ "Tenant Group 6,tenant-group-6,Sixth tenant group,Sixth tenant group comment",
)
cls.csv_update_data = (
- "id,name,description",
- f"{tenant_groups[0].pk},Tenant Group 7,Fourth tenant group7",
- f"{tenant_groups[1].pk},Tenant Group 8,Fifth tenant group8",
- f"{tenant_groups[2].pk},Tenant Group 0,Sixth tenant group9",
+ "id,name,description,comments",
+ f"{tenant_groups[0].pk},Tenant Group 7,Fourth tenant group7,Group 7 comment",
+ f"{tenant_groups[1].pk},Tenant Group 8,Fifth tenant group8,",
+ f"{tenant_groups[2].pk},Tenant Group 0,Sixth tenant group9,",
)
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'New comment',
}
@@ -106,7 +108,7 @@ class ContactGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
def setUpTestData(cls):
contact_groups = (
- ContactGroup(name='Contact Group 1', slug='contact-group-1'),
+ ContactGroup(name='Contact Group 1', slug='contact-group-1', comments='Comment 1'),
ContactGroup(name='Contact Group 2', slug='contact-group-2'),
ContactGroup(name='Contact Group 3', slug='contact-group-3'),
)
@@ -120,24 +122,26 @@ class ContactGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'slug': 'contact-group-x',
'description': 'A new contact group',
'tags': [t.pk for t in tags],
+ 'comments': 'Form data comment',
}
cls.csv_data = (
- "name,slug,description",
- "Contact Group 4,contact-group-4,Fourth contact group",
- "Contact Group 5,contact-group-5,Fifth contact group",
- "Contact Group 6,contact-group-6,Sixth contact group",
+ "name,slug,description,comments",
+ "Contact Group 4,contact-group-4,Fourth contact group,",
+ "Contact Group 5,contact-group-5,Fifth contact group,Fifth comment",
+ "Contact Group 6,contact-group-6,Sixth contact group,",
)
cls.csv_update_data = (
- "id,name,description",
- f"{contact_groups[0].pk},Contact Group 7,Fourth contact group7",
- f"{contact_groups[1].pk},Contact Group 8,Fifth contact group8",
- f"{contact_groups[2].pk},Contact Group 0,Sixth contact group9",
+ "id,name,description,comments",
+ f"{contact_groups[0].pk},Contact Group 7,Fourth contact group7,",
+ f"{contact_groups[1].pk},Contact Group 8,Fifth contact group8,Group 8 comment",
+ f"{contact_groups[2].pk},Contact Group 0,Sixth contact group9,",
)
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'Bulk update comment',
}
diff --git a/netbox/wireless/api/serializers_/wirelesslans.py b/netbox/wireless/api/serializers_/wirelesslans.py
index 68f79daf6..97d57f9f5 100644
--- a/netbox/wireless/api/serializers_/wirelesslans.py
+++ b/netbox/wireless/api/serializers_/wirelesslans.py
@@ -26,7 +26,7 @@ class WirelessLANGroupSerializer(NestedGroupModelSerializer):
model = WirelessLANGroup
fields = [
'id', 'url', 'display_url', 'display', 'name', 'slug', 'parent', 'description', 'tags', 'custom_fields',
- 'created', 'last_updated', 'wirelesslan_count', '_depth',
+ 'created', 'last_updated', 'wirelesslan_count', 'comments', '_depth',
]
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'wirelesslan_count', '_depth')
diff --git a/netbox/wireless/filtersets.py b/netbox/wireless/filtersets.py
index cc5aefbd8..bd96865ad 100644
--- a/netbox/wireless/filtersets.py
+++ b/netbox/wireless/filtersets.py
@@ -5,7 +5,7 @@ from dcim.choices import LinkStatusChoices
from dcim.base_filtersets import ScopedFilterSet
from dcim.models import Interface
from ipam.models import VLAN
-from netbox.filtersets import OrganizationalModelFilterSet, NetBoxModelFilterSet
+from netbox.filtersets import NestedGroupModelFilterSet, NetBoxModelFilterSet
from tenancy.filtersets import TenancyFilterSet
from utilities.filters import TreeNodeMultipleChoiceFilter
from .choices import *
@@ -18,7 +18,7 @@ __all__ = (
)
-class WirelessLANGroupFilterSet(OrganizationalModelFilterSet):
+class WirelessLANGroupFilterSet(NestedGroupModelFilterSet):
parent_id = django_filters.ModelMultipleChoiceFilter(
queryset=WirelessLANGroup.objects.all()
)
diff --git a/netbox/wireless/forms/bulk_edit.py b/netbox/wireless/forms/bulk_edit.py
index 5cd3a157a..1a75512e1 100644
--- a/netbox/wireless/forms/bulk_edit.py
+++ b/netbox/wireless/forms/bulk_edit.py
@@ -32,12 +32,13 @@ class WirelessLANGroupBulkEditForm(NetBoxModelBulkEditForm):
max_length=200,
required=False
)
+ comments = CommentField()
model = WirelessLANGroup
fieldsets = (
FieldSet('parent', 'description'),
)
- nullable_fields = ('parent', 'description')
+ nullable_fields = ('parent', 'description', 'comments')
class WirelessLANBulkEditForm(ScopedBulkEditForm, NetBoxModelBulkEditForm):
diff --git a/netbox/wireless/forms/bulk_import.py b/netbox/wireless/forms/bulk_import.py
index 1fece7e46..389dcf25d 100644
--- a/netbox/wireless/forms/bulk_import.py
+++ b/netbox/wireless/forms/bulk_import.py
@@ -30,7 +30,7 @@ class WirelessLANGroupImportForm(NetBoxModelImportForm):
class Meta:
model = WirelessLANGroup
- fields = ('name', 'slug', 'parent', 'description', 'tags')
+ fields = ('name', 'slug', 'parent', 'description', 'tags', 'comments')
class WirelessLANImportForm(ScopedImportForm, NetBoxModelImportForm):
diff --git a/netbox/wireless/forms/model_forms.py b/netbox/wireless/forms/model_forms.py
index 9cfcca7ba..56422ab57 100644
--- a/netbox/wireless/forms/model_forms.py
+++ b/netbox/wireless/forms/model_forms.py
@@ -24,6 +24,7 @@ class WirelessLANGroupForm(NetBoxModelForm):
required=False
)
slug = SlugField()
+ comments = CommentField()
fieldsets = (
FieldSet('parent', 'name', 'slug', 'description', 'tags', name=_('Wireless LAN Group')),
@@ -32,7 +33,7 @@ class WirelessLANGroupForm(NetBoxModelForm):
class Meta:
model = WirelessLANGroup
fields = [
- 'parent', 'name', 'slug', 'description', 'tags',
+ 'parent', 'name', 'slug', 'description', 'tags', 'comments',
]
diff --git a/netbox/wireless/migrations/0014_wirelesslangroup_comments.py b/netbox/wireless/migrations/0014_wirelesslangroup_comments.py
new file mode 100644
index 000000000..9fc1a99d6
--- /dev/null
+++ b/netbox/wireless/migrations/0014_wirelesslangroup_comments.py
@@ -0,0 +1,16 @@
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wireless', '0013_natural_ordering'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='wirelesslangroup',
+ name='comments',
+ field=models.TextField(blank=True),
+ ),
+ ]
diff --git a/netbox/wireless/search.py b/netbox/wireless/search.py
index e1be53c09..3c1565cb7 100644
--- a/netbox/wireless/search.py
+++ b/netbox/wireless/search.py
@@ -21,6 +21,7 @@ class WirelessLANGroupIndex(SearchIndex):
('name', 100),
('slug', 110),
('description', 500),
+ ('comments', 5000),
)
display_attrs = ('description',)
diff --git a/netbox/wireless/tests/test_api.py b/netbox/wireless/tests/test_api.py
index f768eafaf..0fe5e45f6 100644
--- a/netbox/wireless/tests/test_api.py
+++ b/netbox/wireless/tests/test_api.py
@@ -24,10 +24,12 @@ class WirelessLANGroupTest(APIViewTestCases.APIViewTestCase):
{
'name': 'Wireless LAN Group 4',
'slug': 'wireless-lan-group-4',
+ 'comments': '',
},
{
'name': 'Wireless LAN Group 5',
'slug': 'wireless-lan-group-5',
+ 'comments': 'LAN Group 5 comment',
},
{
'name': 'Wireless LAN Group 6',
@@ -36,6 +38,7 @@ class WirelessLANGroupTest(APIViewTestCases.APIViewTestCase):
]
bulk_update_data = {
'description': 'New description',
+ 'comments': 'New comment',
}
@classmethod
diff --git a/netbox/wireless/tests/test_filtersets.py b/netbox/wireless/tests/test_filtersets.py
index 27aab83d8..9e8905d4a 100644
--- a/netbox/wireless/tests/test_filtersets.py
+++ b/netbox/wireless/tests/test_filtersets.py
@@ -21,7 +21,10 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
parent_groups = (
WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1', description='A'),
WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2', description='B'),
- WirelessLANGroup(name='Wireless LAN Group 3', slug='wireless-lan-group-3', description='C'),
+ WirelessLANGroup(
+ name='Wireless LAN Group 3', slug='wireless-lan-group-3', description='C',
+ comments='Parent Group 3 comment',
+ ),
)
for group in parent_groups:
group.save()
@@ -38,10 +41,15 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
slug='wireless-lan-group-1b',
parent=parent_groups[0],
description='foobar2',
+ comments='Child Group 1B comment',
),
WirelessLANGroup(name='Wireless LAN Group 2A', slug='wireless-lan-group-2a', parent=parent_groups[1]),
WirelessLANGroup(name='Wireless LAN Group 2B', slug='wireless-lan-group-2b', parent=parent_groups[1]),
- WirelessLANGroup(name='Wireless LAN Group 3A', slug='wireless-lan-group-3a', parent=parent_groups[2]),
+ WirelessLANGroup(
+ name='Wireless LAN Group 3A', slug='wireless-lan-group-3a', parent=parent_groups[2],
+ comments='Wireless LAN Group 3A comment',
+
+ ),
WirelessLANGroup(name='Wireless LAN Group 3B', slug='wireless-lan-group-3b', parent=parent_groups[2]),
)
for group in groups:
@@ -62,6 +70,13 @@ class WirelessLANGroupTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'q': 'foobar1'}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+ def test_q_comments(self):
+ params = {'q': 'parent'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
+
+ params = {'q': 'comment'}
+ self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
+
def test_name(self):
params = {'name': ['Wireless LAN Group 1', 'Wireless LAN Group 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
diff --git a/netbox/wireless/tests/test_views.py b/netbox/wireless/tests/test_views.py
index 51af37364..975f18c0d 100644
--- a/netbox/wireless/tests/test_views.py
+++ b/netbox/wireless/tests/test_views.py
@@ -16,7 +16,9 @@ class WirelessLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
groups = (
WirelessLANGroup(name='Wireless LAN Group 1', slug='wireless-lan-group-1'),
- WirelessLANGroup(name='Wireless LAN Group 2', slug='wireless-lan-group-2'),
+ WirelessLANGroup(
+ name='Wireless LAN Group 2', slug='wireless-lan-group-2', comments='LAN Group 2 comment',
+ ),
WirelessLANGroup(name='Wireless LAN Group 3', slug='wireless-lan-group-3'),
)
for group in groups:
@@ -30,24 +32,26 @@ class WirelessLANGroupTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
'parent': groups[2].pk,
'description': 'A new wireless LAN group',
'tags': [t.pk for t in tags],
+ 'comments': 'LAN Group X comment',
}
cls.csv_data = (
- "name,slug,description",
- "Wireless LAN Group 4,wireless-lan-group-4,Fourth wireless LAN group",
- "Wireless LAN Group 5,wireless-lan-group-5,Fifth wireless LAN group",
- "Wireless LAN Group 6,wireless-lan-group-6,Sixth wireless LAN group",
+ "name,slug,description,comments",
+ "Wireless LAN Group 4,wireless-lan-group-4,Fourth wireless LAN group,",
+ "Wireless LAN Group 5,wireless-lan-group-5,Fifth wireless LAN group,",
+ "Wireless LAN Group 6,wireless-lan-group-6,Sixth wireless LAN group,LAN Group 6 comment",
)
cls.csv_update_data = (
- "id,name,description",
- f"{groups[0].pk},Wireless LAN Group 7,Fourth wireless LAN group7",
- f"{groups[1].pk},Wireless LAN Group 8,Fifth wireless LAN group8",
- f"{groups[2].pk},Wireless LAN Group 0,Sixth wireless LAN group9",
+ "id,name,description,comments",
+ f"{groups[0].pk},Wireless LAN Group 7,Fourth wireless LAN group7,Group 7 comment",
+ f"{groups[1].pk},Wireless LAN Group 8,Fifth wireless LAN group8,",
+ f"{groups[2].pk},Wireless LAN Group 0,Sixth wireless LAN group9,",
)
cls.bulk_edit_data = {
'description': 'New description',
+ 'comments': 'New Comments',
}