Răsfoiți Sursa

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
Jason Novinger 11 luni în urmă
părinte
comite
44efd5e833

+ 1 - 1
netbox/dcim/api/serializers_/sites.py

@@ -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')

+ 2 - 1
netbox/dcim/filtersets.py

@@ -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)
         )
         )
 
 
 
 

+ 2 - 1
netbox/dcim/forms/bulk_edit.py

@@ -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):

+ 4 - 1
netbox/dcim/forms/bulk_import.py

@@ -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)

+ 3 - 1
netbox/dcim/forms/model_forms.py

@@ -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',
         )
         )
 
 
 
 

+ 1 - 0
netbox/dcim/search.py

@@ -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')
 
 

+ 1 - 1
netbox/dcim/tables/sites.py

@@ -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'

+ 5 - 0
netbox/dcim/tests/test_api.py

@@ -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',

+ 10 - 0
netbox/dcim/tests/test_filtersets.py

@@ -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)

+ 13 - 8
netbox/dcim/tests/test_views.py

@@ -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 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 = (
         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 = {
         cls.bulk_edit_data = {
             'description': 'New description',
             'description': 'New description',
+            'comments': 'This comment is also really boring',
         }
         }