mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-24 17:38:37 -06:00
Closes #11494: Enable filtering objects by create/update request IDs
This commit is contained in:
parent
5b81986bb3
commit
6e4c4c4342
@ -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
|
* [#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
|
* [#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
|
* [#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
|
* [#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
|
* [#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
|
* [#11625](https://github.com/netbox-community/netbox/issues/11625) - Add HTMX support to ObjectEditView
|
||||||
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
@ -31,7 +31,8 @@ class ObjectChange(models.Model):
|
|||||||
editable=False
|
editable=False
|
||||||
)
|
)
|
||||||
request_id = models.UUIDField(
|
request_id = models.UUIDField(
|
||||||
editable=False
|
editable=False,
|
||||||
|
db_index=True
|
||||||
)
|
)
|
||||||
action = models.CharField(
|
action = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
|
@ -6,6 +6,7 @@ from django.contrib.contenttypes.models import ContentType
|
|||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
from circuits.models import Provider
|
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 DeviceRole, DeviceType, Manufacturer, Platform, Rack, Region, Site, SiteGroup
|
||||||
from dcim.models import Location
|
from dcim.models import Location
|
||||||
from extras.choices import *
|
from extras.choices import *
|
||||||
@ -924,3 +925,71 @@ class ObjectChangeTestCase(TestCase, BaseFilterSetTests):
|
|||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||||
params = {'changed_object_type_id': [ContentType.objects.get(app_label='dcim', model='site').pk]}
|
params = {'changed_object_type_id': [ContentType.objects.get(app_label='dcim', model='site').pk]}
|
||||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
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)
|
||||||
|
@ -7,9 +7,9 @@ from django_filters.exceptions import FieldLookupError
|
|||||||
from django_filters.utils import get_model_field, resolve_field
|
from django_filters.utils import get_model_field, resolve_field
|
||||||
from django.utils.translation import gettext as _
|
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.filters import TagFilter
|
||||||
from extras.models import CustomField, SavedFilter
|
from extras.models import CustomField, ObjectChange, SavedFilter
|
||||||
from utilities.constants import (
|
from utilities.constants import (
|
||||||
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP,
|
FILTER_CHAR_BASED_LOOKUP_MAP, FILTER_NEGATION_LOOKUP_MAP, FILTER_TREENODE_NEGATION_LOOKUP_MAP,
|
||||||
FILTER_NUMERIC_BASED_LOOKUP_MAP
|
FILTER_NUMERIC_BASED_LOOKUP_MAP
|
||||||
@ -231,6 +231,26 @@ class ChangeLoggedModelFilterSet(BaseFilterSet):
|
|||||||
"""
|
"""
|
||||||
created = filters.MultiValueDateTimeFilter()
|
created = filters.MultiValueDateTimeFilter()
|
||||||
last_updated = 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):
|
class NetBoxModelFilterSet(ChangeLoggedModelFilterSet):
|
||||||
|
Loading…
Reference in New Issue
Block a user