mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -06:00
Adds Location.comments field in the required locations
- [x] 1. Add the field to the model class - [x] 2. Generate and run database migrations - [NA] 3. Add validation logic to clean() - [NA] 4. Update relevant querysets - [x] 5. Update API serializer - [x] 6. Add fields to forms - [x] dcim.forms.model_forms.LocationForm, create/edit (e.g. model_forms.py) - [x] dcim.forms.buld_edit.LocationBulkEditForm, bulk edit - [x] dcim.dorms.bulk_import.LocationImportForm, CSV import - [x] filter (UI and API) - [NA] UI - Note: could not find any comments related things in filtersets - [x] API - [x] 7. Extend object filter set - [x] 8. Add column to object table - [x] 9. Update the SearchIndex - [x] 10. Update the UI templates - [x] 11. Create/extend test cases - [NA] models - [x] views - [NA] forms - [x] filtersets - [x] api - [NA] 12. Update the model's documentation
This commit is contained in:
parent
ae7a47ca60
commit
44efd5e833
@ -93,6 +93,6 @@ class LocationSerializer(NestedGroupModelSerializer):
|
|||||||
fields = [
|
fields = [
|
||||||
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
|
'id', 'url', 'display_url', 'display', 'name', 'slug', 'site', 'parent', 'status', 'tenant', 'facility',
|
||||||
'description', 'tags', 'custom_fields', 'created', 'last_updated', 'rack_count', 'device_count',
|
'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')
|
brief_fields = ('id', 'url', 'display', 'name', 'slug', 'description', 'rack_count', '_depth')
|
||||||
|
@ -280,7 +280,8 @@ class LocationFilterSet(TenancyFilterSet, ContactModelFilterSet, OrganizationalM
|
|||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
Q(name__icontains=value) |
|
Q(name__icontains=value) |
|
||||||
Q(facility__icontains=value) |
|
Q(facility__icontains=value) |
|
||||||
Q(description__icontains=value)
|
Q(description__icontains=value) |
|
||||||
|
Q(comments__icontains=value)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,12 +197,13 @@ class LocationBulkEditForm(NetBoxModelBulkEditForm):
|
|||||||
max_length=200,
|
max_length=200,
|
||||||
required=False
|
required=False
|
||||||
)
|
)
|
||||||
|
comments = CommentField()
|
||||||
|
|
||||||
model = Location
|
model = Location
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('site', 'parent', 'status', 'tenant', 'description'),
|
FieldSet('site', 'parent', 'status', 'tenant', 'description'),
|
||||||
)
|
)
|
||||||
nullable_fields = ('parent', 'tenant', 'description')
|
nullable_fields = ('parent', 'tenant', 'description', 'comments')
|
||||||
|
|
||||||
|
|
||||||
class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
|
class RackRoleBulkEditForm(NetBoxModelBulkEditForm):
|
||||||
|
@ -160,7 +160,10 @@ class LocationImportForm(NetBoxModelImportForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
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):
|
def __init__(self, data=None, *args, **kwargs):
|
||||||
super().__init__(data, *args, **kwargs)
|
super().__init__(data, *args, **kwargs)
|
||||||
|
@ -179,6 +179,7 @@ class LocationForm(TenancyForm, NetBoxModelForm):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
slug = SlugField()
|
slug = SlugField()
|
||||||
|
comments = CommentField()
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
|
FieldSet('site', 'parent', 'name', 'slug', 'status', 'facility', 'description', 'tags', name=_('Location')),
|
||||||
@ -188,7 +189,8 @@ class LocationForm(TenancyForm, NetBoxModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant', 'facility', 'tags',
|
'site', 'parent', 'name', 'slug', 'status', 'description', 'tenant_group', 'tenant',
|
||||||
|
'facility', 'tags', 'comments',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ class LocationIndex(SearchIndex):
|
|||||||
('facility', 100),
|
('facility', 100),
|
||||||
('slug', 110),
|
('slug', 110),
|
||||||
('description', 500),
|
('description', 500),
|
||||||
|
('comments', 5000),
|
||||||
)
|
)
|
||||||
display_attrs = ('site', 'status', 'tenant', 'facility', 'description')
|
display_attrs = ('site', 'status', 'tenant', 'facility', 'description')
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ class LocationTable(TenancyColumnsMixin, ContactsColumnMixin, NetBoxTable):
|
|||||||
model = Location
|
model = Location
|
||||||
fields = (
|
fields = (
|
||||||
'pk', 'id', 'name', 'site', 'status', 'facility', 'tenant', 'tenant_group', 'rack_count', 'device_count',
|
'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 = (
|
default_columns = (
|
||||||
'pk', 'name', 'site', 'status', 'facility', 'tenant', 'rack_count', 'device_count', 'description'
|
'pk', 'name', 'site', 'status', 'facility', 'tenant', 'rack_count', 'device_count', 'description'
|
||||||
|
@ -212,12 +212,14 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
name='Parent Location 1',
|
name='Parent Location 1',
|
||||||
slug='parent-location-1',
|
slug='parent-location-1',
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
|
comments='First!'
|
||||||
),
|
),
|
||||||
Location.objects.create(
|
Location.objects.create(
|
||||||
site=sites[1],
|
site=sites[1],
|
||||||
name='Parent Location 2',
|
name='Parent Location 2',
|
||||||
slug='parent-location-2',
|
slug='parent-location-2',
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
|
comments='Second!'
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -227,6 +229,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
slug='location-1',
|
slug='location-1',
|
||||||
parent=parent_locations[0],
|
parent=parent_locations[0],
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
|
comments='Third!'
|
||||||
)
|
)
|
||||||
Location.objects.create(
|
Location.objects.create(
|
||||||
site=sites[0],
|
site=sites[0],
|
||||||
@ -250,6 +253,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'parent': parent_locations[1].pk,
|
'parent': parent_locations[1].pk,
|
||||||
'status': LocationStatusChoices.STATUS_PLANNED,
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
|
'comments': '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Test Location 5',
|
'name': 'Test Location 5',
|
||||||
@ -257,6 +261,7 @@ class LocationTest(APIViewTestCases.APIViewTestCase):
|
|||||||
'site': sites[1].pk,
|
'site': sites[1].pk,
|
||||||
'parent': parent_locations[1].pk,
|
'parent': parent_locations[1].pk,
|
||||||
'status': LocationStatusChoices.STATUS_PLANNED,
|
'status': LocationStatusChoices.STATUS_PLANNED,
|
||||||
|
'comments': 'Somebody should check on this location',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'Test Location 6',
|
'name': 'Test Location 6',
|
||||||
|
@ -401,6 +401,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
status=LocationStatusChoices.STATUS_PLANNED,
|
status=LocationStatusChoices.STATUS_PLANNED,
|
||||||
facility='Facility 1',
|
facility='Facility 1',
|
||||||
description='foobar1',
|
description='foobar1',
|
||||||
|
comments='',
|
||||||
),
|
),
|
||||||
Location(
|
Location(
|
||||||
name='Location 2A',
|
name='Location 2A',
|
||||||
@ -410,6 +411,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
status=LocationStatusChoices.STATUS_STAGING,
|
status=LocationStatusChoices.STATUS_STAGING,
|
||||||
facility='Facility 2',
|
facility='Facility 2',
|
||||||
description='foobar2',
|
description='foobar2',
|
||||||
|
comments='First comment!',
|
||||||
),
|
),
|
||||||
Location(
|
Location(
|
||||||
name='Location 3A',
|
name='Location 3A',
|
||||||
@ -419,6 +421,7 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
status=LocationStatusChoices.STATUS_DECOMMISSIONING,
|
status=LocationStatusChoices.STATUS_DECOMMISSIONING,
|
||||||
facility='Facility 3',
|
facility='Facility 3',
|
||||||
description='foobar3',
|
description='foobar3',
|
||||||
|
comments='_This_ is a **bold comment**',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for location in locations:
|
for location in locations:
|
||||||
@ -436,6 +439,13 @@ class LocationTestCase(TestCase, ChangeLoggedFilterSetTests):
|
|||||||
params = {'q': 'foobar1'}
|
params = {'q': 'foobar1'}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
|
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):
|
def test_name(self):
|
||||||
params = {'name': ['Location 1', 'Location 2']}
|
params = {'name': ['Location 1', 'Location 2']}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||||
|
@ -202,6 +202,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
site=site,
|
site=site,
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
tenant=tenant,
|
tenant=tenant,
|
||||||
|
comments='',
|
||||||
),
|
),
|
||||||
Location(
|
Location(
|
||||||
name='Location 2',
|
name='Location 2',
|
||||||
@ -209,6 +210,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
site=site,
|
site=site,
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
tenant=tenant,
|
tenant=tenant,
|
||||||
|
comments='First comment!',
|
||||||
),
|
),
|
||||||
Location(
|
Location(
|
||||||
name='Location 3',
|
name='Location 3',
|
||||||
@ -216,6 +218,7 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
site=site,
|
site=site,
|
||||||
status=LocationStatusChoices.STATUS_ACTIVE,
|
status=LocationStatusChoices.STATUS_ACTIVE,
|
||||||
tenant=tenant,
|
tenant=tenant,
|
||||||
|
comments='_This_ is a **bold comment**',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for location in locations:
|
for location in locations:
|
||||||
@ -232,24 +235,26 @@ class LocationTestCase(ViewTestCases.OrganizationalObjectViewTestCase):
|
|||||||
'tenant': tenant.pk,
|
'tenant': tenant.pk,
|
||||||
'description': 'A new location',
|
'description': 'A new location',
|
||||||
'tags': [t.pk for t in tags],
|
'tags': [t.pk for t in tags],
|
||||||
|
'comments': 'This comment is really boring',
|
||||||
}
|
}
|
||||||
|
|
||||||
cls.csv_data = (
|
cls.csv_data = (
|
||||||
"site,tenant,name,slug,status,description",
|
"site,tenant,name,slug,status,description,comments",
|
||||||
"Site 1,Tenant 1,Location 4,location-4,planned,Fourth location",
|
"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 5,location-5,planned,Fifth location,",
|
||||||
"Site 1,Tenant 1,Location 6,location-6,planned,Sixth location",
|
"Site 1,Tenant 1,Location 6,location-6,planned,Sixth location,hi!",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.csv_update_data = (
|
cls.csv_update_data = (
|
||||||
"id,name,description",
|
"id,name,description,comments",
|
||||||
f"{locations[0].pk},Location 7,Fourth location7",
|
f"{locations[0].pk},Location 7,Fourth location7,Useful comment",
|
||||||
f"{locations[1].pk},Location 8,Fifth location8",
|
f"{locations[1].pk},Location 8,Fifth location8,unuseful comment",
|
||||||
f"{locations[2].pk},Location 0,Sixth location9",
|
f"{locations[2].pk},Location 0,Sixth location9,",
|
||||||
)
|
)
|
||||||
|
|
||||||
cls.bulk_edit_data = {
|
cls.bulk_edit_data = {
|
||||||
'description': 'New description',
|
'description': 'New description',
|
||||||
|
'comments': 'This comment is also really boring',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user