mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Closes #4897: Allow filtering by content type identified as <app>.<model> string
This commit is contained in:
@@ -4,7 +4,7 @@ from django.db.models import Q
|
||||
|
||||
from dcim.models import DeviceRole, Platform, Region, Site
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from utilities.filters import BaseFilterSet
|
||||
from utilities.filters import BaseFilterSet, ContentTypeFilter
|
||||
from virtualization.models import Cluster, ClusterGroup
|
||||
from .choices import *
|
||||
from .models import ConfigContext, CustomField, ExportTemplate, ImageAttachment, JobResult, ObjectChange, Tag
|
||||
@@ -81,10 +81,11 @@ class ExportTemplateFilterSet(BaseFilterSet):
|
||||
|
||||
|
||||
class ImageAttachmentFilterSet(BaseFilterSet):
|
||||
content_type = ContentTypeFilter()
|
||||
|
||||
class Meta:
|
||||
model = ImageAttachment
|
||||
fields = ['id', 'content_type', 'object_id', 'name']
|
||||
fields = ['id', 'content_type_id', 'object_id', 'name']
|
||||
|
||||
|
||||
class TagFilterSet(BaseFilterSet):
|
||||
@@ -234,11 +235,12 @@ class ObjectChangeFilterSet(BaseFilterSet):
|
||||
label='Search',
|
||||
)
|
||||
time = django_filters.DateTimeFromToRangeFilter()
|
||||
changed_object_type = ContentTypeFilter()
|
||||
|
||||
class Meta:
|
||||
model = ObjectChange
|
||||
fields = [
|
||||
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type', 'changed_object_id',
|
||||
'id', 'user', 'user_name', 'request_id', 'action', 'changed_object_type_id', 'changed_object_id',
|
||||
'object_repr',
|
||||
]
|
||||
|
||||
|
@@ -361,8 +361,8 @@ class ObjectChangeFilterForm(BootstrapMixin, forms.Form):
|
||||
api_url='/api/users/users/',
|
||||
)
|
||||
)
|
||||
changed_object_type = forms.ModelChoiceField(
|
||||
queryset=ContentType.objects.order_by('model'),
|
||||
changed_object_type_id = forms.ModelChoiceField(
|
||||
queryset=ContentType.objects.order_by('app_label', 'model'),
|
||||
required=False,
|
||||
widget=ContentTypeSelect(),
|
||||
label='Object Type'
|
||||
|
@@ -1,9 +1,14 @@
|
||||
import uuid
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from dcim.models import DeviceRole, Platform, Rack, Region, Site
|
||||
from extras.choices import ObjectChangeActionChoices
|
||||
from extras.filters import *
|
||||
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, Tag
|
||||
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, Tag
|
||||
from ipam.models import IPAddress
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from virtualization.models import Cluster, ClusterGroup, ClusterType
|
||||
|
||||
@@ -298,4 +303,98 @@ class TagTestCase(TestCase):
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
|
||||
|
||||
|
||||
# TODO: ObjectChangeFilter test
|
||||
class ObjectChangeTestCase(TestCase):
|
||||
queryset = ObjectChange.objects.all()
|
||||
filterset = ObjectChangeFilterSet
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
users = (
|
||||
User(username='user1'),
|
||||
User(username='user2'),
|
||||
User(username='user3'),
|
||||
)
|
||||
User.objects.bulk_create(users)
|
||||
|
||||
site = Site.objects.create(name='Test Site 1', slug='test-site-1')
|
||||
ipaddress = IPAddress.objects.create(address='192.0.2.1/24')
|
||||
|
||||
object_changes = (
|
||||
ObjectChange(
|
||||
user=users[0],
|
||||
user_name=users[0].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||
changed_object=site,
|
||||
object_repr=str(site),
|
||||
object_data={'name': site.name, 'slug': site.slug}
|
||||
),
|
||||
ObjectChange(
|
||||
user=users[0],
|
||||
user_name=users[0].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_UPDATE,
|
||||
changed_object=site,
|
||||
object_repr=str(site),
|
||||
object_data={'name': site.name, 'slug': site.slug}
|
||||
),
|
||||
ObjectChange(
|
||||
user=users[1],
|
||||
user_name=users[1].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_DELETE,
|
||||
changed_object=site,
|
||||
object_repr=str(site),
|
||||
object_data={'name': site.name, 'slug': site.slug}
|
||||
),
|
||||
ObjectChange(
|
||||
user=users[1],
|
||||
user_name=users[1].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_CREATE,
|
||||
changed_object=ipaddress,
|
||||
object_repr=str(ipaddress),
|
||||
object_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||
),
|
||||
ObjectChange(
|
||||
user=users[2],
|
||||
user_name=users[2].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_UPDATE,
|
||||
changed_object=ipaddress,
|
||||
object_repr=str(ipaddress),
|
||||
object_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||
),
|
||||
ObjectChange(
|
||||
user=users[2],
|
||||
user_name=users[2].username,
|
||||
request_id=uuid.uuid4(),
|
||||
action=ObjectChangeActionChoices.ACTION_DELETE,
|
||||
changed_object=ipaddress,
|
||||
object_repr=str(ipaddress),
|
||||
object_data={'address': ipaddress.address, 'status': ipaddress.status}
|
||||
),
|
||||
)
|
||||
ObjectChange.objects.bulk_create(object_changes)
|
||||
|
||||
def test_id(self):
|
||||
params = {'id': self.queryset.values_list('pk', flat=True)[:3]}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
# def test_user(self):
|
||||
# params = {'user_id': User.objects.filter(username__in=['user1', 'user2']).values_list('pk', flat=True)}
|
||||
# self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
# params = {'user': ['user1', 'user2']}
|
||||
# self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_user_name(self):
|
||||
params = {'user_name': ['user1', 'user2']}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 4)
|
||||
|
||||
def test_changed_object_type(self):
|
||||
params = {'changed_object_type': 'dcim.site'}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
||||
def test_changed_object_type_id(self):
|
||||
params = {'changed_object_type_id': ContentType.objects.get(app_label='dcim', model='site').pk}
|
||||
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import django_filters
|
||||
from django_filters.constants import EMPTY_VALUES
|
||||
from copy import deepcopy
|
||||
from dcim.forms import MACAddressField
|
||||
from django import forms
|
||||
@@ -115,6 +116,26 @@ class NumericArrayFilter(django_filters.NumberFilter):
|
||||
return super().filter(qs, value)
|
||||
|
||||
|
||||
class ContentTypeFilter(django_filters.CharFilter):
|
||||
"""
|
||||
Allow specifying a ContentType by <app_label>.<model> (e.g. "dcim.site").
|
||||
"""
|
||||
def filter(self, qs, value):
|
||||
if value in EMPTY_VALUES:
|
||||
return qs
|
||||
|
||||
try:
|
||||
app_label, model = value.lower().split('.')
|
||||
except ValueError:
|
||||
return qs.none()
|
||||
return qs.filter(
|
||||
**{
|
||||
f'{self.field_name}__app_label': app_label,
|
||||
f'{self.field_name}__model': model
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# FilterSets
|
||||
#
|
||||
|
Reference in New Issue
Block a user