mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Merge pull request #18859 from netbox-community/17602-comments-field-for-nested-models
Closes #17602: adds comments field to NestedGroupModel children
This commit is contained in:
commit
cd10087b2b
@ -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')
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
)
|
||||
|
||||
|
||||
|
@ -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),
|
||||
),
|
||||
]
|
@ -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')
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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',
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -150,6 +150,10 @@ class NestedGroupModel(NetBoxFeatureSet, MPTTModel):
|
||||
max_length=200,
|
||||
blank=True
|
||||
)
|
||||
comments = models.TextField(
|
||||
verbose_name=_('comments'),
|
||||
blank=True
|
||||
)
|
||||
|
||||
objects = TreeManager()
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -41,6 +41,7 @@
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -41,6 +41,7 @@
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/custom_fields.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -32,6 +32,7 @@
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -40,6 +40,7 @@
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -40,6 +40,7 @@
|
||||
</table>
|
||||
</div>
|
||||
{% include 'inc/panels/tags.html' %}
|
||||
{% include 'inc/panels/comments.html' %}
|
||||
{% plugin_left_page object %}
|
||||
</div>
|
||||
<div class="col col-md-6">
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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)'),
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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),
|
||||
),
|
||||
]
|
@ -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',)
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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',
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
}
|
||||
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
]
|
||||
|
||||
|
||||
|
16
netbox/wireless/migrations/0014_wirelesslangroup_comments.py
Normal file
16
netbox/wireless/migrations/0014_wirelesslangroup_comments.py
Normal file
@ -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),
|
||||
),
|
||||
]
|
@ -21,6 +21,7 @@ class WirelessLANGroupIndex(SearchIndex):
|
||||
('name', 100),
|
||||
('slug', 110),
|
||||
('description', 500),
|
||||
('comments', 5000),
|
||||
)
|
||||
display_attrs = ('description',)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user