Skip to content

Commit

Permalink
Closes #11494: Enable filtering objects by create/update request IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremystretch committed Mar 16, 2023
1 parent 5b81986 commit 6e4c4c4
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/release-notes/version-3.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ A new ASN range model has been introduced to facilitate the provisioning of new
* [#10729](https://github.com/netbox-community/netbox/issues/10729) - Add date & time custom field type
* [#11254](https://github.com/netbox-community/netbox/issues/11254) - Introduce the `X-Request-ID` HTTP header to annotate the unique ID of each request for change logging
* [#11440](https://github.com/netbox-community/netbox/issues/11440) - Add an `enabled` field for device type interfaces
* [#11494](https://github.com/netbox-community/netbox/issues/11494) - Enable filtering objects by create/update request IDs
* [#11517](https://github.com/netbox-community/netbox/issues/11517) - Standardize the inclusion of related objects across the entire UI
* [#11584](https://github.com/netbox-community/netbox/issues/11584) - Add a list view for contact assignments
* [#11625](https://github.com/netbox-community/netbox/issues/11625) - Add HTMX support to ObjectEditView
Expand Down
18 changes: 18 additions & 0 deletions netbox/extras/migrations/0090_objectchange_index_request_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.7 on 2023-03-16 20:06

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('extras', '0089_customfield_is_cloneable'),
]

operations = [
migrations.AlterField(
model_name='objectchange',
name='request_id',
field=models.UUIDField(db_index=True, editable=False),
),
]
3 changes: 2 additions & 1 deletion netbox/extras/models/change_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class ObjectChange(models.Model):
editable=False
)
request_id = models.UUIDField(
editable=False
editable=False,
db_index=True
)
action = models.CharField(
max_length=50,
Expand Down
69 changes: 69 additions & 0 deletions netbox/extras/tests/test_filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.test import TestCase

from circuits.models import Provider
from dcim.filtersets import SiteFilterSet
from dcim.models import DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup
from dcim.models import Location
from extras.choices import *
Expand Down Expand Up @@ -924,3 +925,71 @@ def test_changed_object_type(self):
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
params = {'changed_object_type_id': [ContentType.objects.get(app_label='dcim', model='site').pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)


class ChangeLoggedFilterSetTestCase(TestCase):
"""
Evaluate base ChangeLoggedFilterSet filters using the Site model.
"""
queryset = Site.objects.all()
filterset = SiteFilterSet

@classmethod
def setUpTestData(cls):
content_type = ContentType.objects.get_for_model(Site)

# Create three sites
sites = (
Site(name='Site 1', slug='site-1'),
Site(name='Site 2', slug='site-2'),
Site(name='Site 3', slug='site-3'),
)
Site.objects.bulk_create(sites)

# Simulate *creation* changelog records for two of the sites
request_id = uuid.uuid4()
objectchanges = (
ObjectChange(
changed_object_type=content_type,
changed_object_id=sites[0].pk,
action=ObjectChangeActionChoices.ACTION_CREATE,
request_id=request_id
),
ObjectChange(
changed_object_type=content_type,
changed_object_id=sites[1].pk,
action=ObjectChangeActionChoices.ACTION_CREATE,
request_id=request_id
),
)
ObjectChange.objects.bulk_create(objectchanges)

# Simulate *update* changelog records for two of the sites
request_id = uuid.uuid4()
objectchanges = (
ObjectChange(
changed_object_type=content_type,
changed_object_id=sites[0].pk,
action=ObjectChangeActionChoices.ACTION_UPDATE,
request_id=request_id
),
ObjectChange(
changed_object_type=content_type,
changed_object_id=sites[1].pk,
action=ObjectChangeActionChoices.ACTION_UPDATE,
request_id=request_id
),
)
ObjectChange.objects.bulk_create(objectchanges)

def test_created_by_request(self):
request_id = ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_CREATE).first().request_id
params = {'created_by_request': request_id}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
self.assertEqual(self.queryset.count(), 3)

def test_updated_by_request(self):
request_id = ObjectChange.objects.filter(action=ObjectChangeActionChoices.ACTION_UPDATE).first().request_id
params = {'updated_by_request': request_id}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
self.assertEqual(self.queryset.count(), 3)
24 changes: 22 additions & 2 deletions netbox/netbox/filtersets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from django_filters.utils import get_model_field, resolve_field
from django.utils.translation import gettext as _

from extras.choices import CustomFieldFilterLogicChoices
from extras.choices import CustomFieldFilterLogicChoices, ObjectChangeActionChoices
from extras.filters import TagFilter
from extras.models import CustomField, SavedFilter
from extras.models import CustomField, ObjectChange, SavedFilter
from utilities.constants import (
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP,
FILTER_NUMERIC_BASED_LOOKUP_MAP
Expand Down Expand Up @@ -231,6 +231,26 @@ class ChangeLoggedModelFilterSet(BaseFilterSet):
"""
created = filters.MultiValueDateTimeFilter()
last_updated = filters.MultiValueDateTimeFilter()
created_by_request = django_filters.UUIDFilter(
method='filter_by_request'
)
updated_by_request = django_filters.UUIDFilter(
method='filter_by_request'
)

def filter_by_request(self, queryset, name, value):
content_type = ContentType.objects.get_for_model(self.Meta.model)
action = {
'created_by_request': ObjectChangeActionChoices.ACTION_CREATE,
'updated_by_request': ObjectChangeActionChoices.ACTION_UPDATE,
}.get(name)
request_id = value
pks = ObjectChange.objects.filter(
changed_object_type=content_type,
action=action,
request_id=request_id
).values_list('changed_object_id', flat=True)
return queryset.filter(pk__in=pks)


class NetBoxModelFilterSet(ChangeLoggedModelFilterSet):
Expand Down

0 comments on commit 6e4c4c4

Please sign in to comment.