Closes #11494: Enable filtering objects by create/update request IDs

This commit is contained in:
jeremystretch 2023-03-16 16:29:43 -04:00
parent 5b81986bb3
commit 6e4c4c4342
5 changed files with 112 additions and 3 deletions

View File

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

View File

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

View File

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

View File

@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
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 *
@ -924,3 +925,71 @@ class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
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)

View File

@ -7,9 +7,9 @@ from django_filters.exceptions import FieldLookupError
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
@ -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):