fix(circuits): Add member_type_id filter to CircuitGroupAssignment

Adds `member_type_id` filter field to support filtering by
ContentType ID alongside existing `member_type` filter. Includes
test coverage verifying correct filtering when member IDs overlap across
different Content Types.

Fixes #22800
This commit is contained in:
Martin Hauser 2026-07-29 15:07:42 +02:00
parent 10ac9ffe8d
commit 29860aaf76
No known key found for this signature in database
2 changed files with 31 additions and 1 deletions

View File

@ -405,6 +405,11 @@ class CircuitGroupAssignmentFilterSet(NetBoxModelFilterSet):
label=_('Search'),
)
member_type = MultiValueContentTypeFilter()
member_type_id = django_filters.ModelMultipleChoiceFilter(
field_name='member_type',
queryset=ContentType.objects.all(),
distinct=False,
)
circuit = MultiValueCharFilter(
method='filter_circuit',
field_name='cid',
@ -450,7 +455,7 @@ class CircuitGroupAssignmentFilterSet(NetBoxModelFilterSet):
class Meta:
model = CircuitGroupAssignment
fields = ('id', 'member_id', 'priority')
fields = ('id', 'member_type_id', 'member_id', 'priority')
def search(self, queryset, name, value):
if not value.strip():

View File

@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from circuits.choices import *
@ -779,6 +780,30 @@ class CircuitGroupAssignmentTestCase(TestCase, ChangeLoggedFilterSetTests):
params = {'virtual_circuit': [virtual_circuits[0].cid, virtual_circuits[1].cid]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_member_type(self):
params = {'member_type': ['circuits.circuit']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
params = {'member_type_id': [ContentType.objects.get_for_model(Circuit).pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
params = {'member_type_id': [ContentType.objects.get_for_model(VirtualCircuit).pk]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 3)
def test_member(self):
"""The member type and ID are matched together, so a matching ID of another type is excluded."""
circuit = Circuit.objects.first()
circuit_type = ContentType.objects.get_for_model(Circuit)
virtual_circuit_type = ContentType.objects.get_for_model(VirtualCircuit)
expected = self.queryset.get(member_type=circuit_type, member_id=circuit.pk)
# A virtual circuit assignment sharing the circuit's object ID must not match
group = CircuitGroup.objects.create(name='Circuit Group 4', slug='circuit-group-4')
CircuitGroupAssignment.objects.create(
group=group, member_type=virtual_circuit_type, member_id=circuit.pk
)
params = {'member_type_id': [circuit_type.pk], 'member_id': [circuit.pk]}
self.assertEqual(list(self.filterset(params, self.queryset).qs), [expected])
def test_provider(self):
providers = Provider.objects.all()[:2]
params = {'provider_id': [providers[0].pk, providers[1].pk]}