You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Nautobot version (Docker tag too if applicable): 1.4.7
Python version: 3.10
Database platform, version: n/a
Middleware(s): n/a
Steps to Reproduce
This is a follow-up to #2665, which fixed the ability for Dynamic Groups to filter properly on nested object types like Location and Region by augmenting TreeNodeMultipleChoiceFilter.filter() to iterate the tree descendants (e.g. Location A --> Location B).
Create Locations
Create a LocationType called My Region and content-type dcim | device
Create a LocationType called My Site with parent set to LocationType("MyRegion") and content-type dcim | device
Create a new Location with LocationType of My Region called Location A with site AMS01 (which is part of the demo dataset)
Create a new Location with LocationType of My Region --> MySite called Location B with parent Location A
Create Devices
Create a new Device named new-device1 with site AMS01 and location set to Location A
Create a new Device named new-device2 with site AMS01 and location set to Location B
Create Dynamic Groups
Create a DynamicGroup with a filter for a location = "Location A" named devices-location-a
Create a DynamicGroup with an empty filter as a parent group named location-parent
Edit location-parent and set the first child group to devices-location-a with operator Include (OR).
Expected Behavior
Navigate to Dynamic Groups list view
Observe that devices-location-a has 2 members
Observe that location-parent also has 2 members
Observed Behavior
Observe that devices-location-a has 2 members
Observe that location-parent only has 1 member
What is happening in the workaround from #2665 is:
DynamicGroup.get_queryset() only uses the filterset to identify membership
DynamicGroup.get_group_queryset() instead relies on aggregated generated Q() objects derived from all child groups
DynamicGroup.members was augmented to return get_queryset() if there are no child groups as a workaround to make filtering on tree objects like Location work, as evident in devices-location-2 having 2 members from Location A and Location B (which is a child of Location A)
Nested Dynamic Groups with children still fallback to the old behavior
The root cause is in the difference between forward lookup using filtersets (e.g. {"location": ["location-a"]}. This will filter as expected because the TreeNodeMultipleChoiceFilter is doing recursive descent for all child locations...
Compared to the reverse lookup, which is using generated Q() objects, (e.g. location__slug='location-a') which you can see IS NOT recursively descending. In the case of the location-parent, observe that the correct generated Q object should be (location__slug='location-a' OR location__slug='location-b').
TL;DR
Simple dynamic groups (no children) use forward filtering based on filtersets
Advanced dynamic groups (with children) use reverse filtering based on Q objects
TreeNodeMultipleChoiceFilter needs to be augmented to correctly generate a nested Q object for any descendants using the generate_query() pattern (See: SearchFilter.generate_query()
DynamicGroup.generate_query_for_filter() needs to be augmented to look for generate_query() methods on Filter instances when it is building nested Q objects for advanced dynamic group. It currently type checks if the filter is an instance of django_filters.MultipleChoiceFilter and calls get_filter_predicate() for each value, but this isn't sufficient for tree node filters.
The text was updated successfully, but these errors were encountered:
Environment
Steps to Reproduce
This is a follow-up to #2665, which fixed the ability for Dynamic Groups to filter properly on nested object types like
Location
andRegion
by augmentingTreeNodeMultipleChoiceFilter.filter()
to iterate the tree descendants (e.g.Location A --> Location B
).Create Locations
LocationType
calledMy Region
and content-typedcim | device
LocationType
calledMy Site
with parent set toLocationType("MyRegion")
and content-typedcim | device
Location
withLocationType
ofMy Region
calledLocation A
with siteAMS01
(which is part of the demo dataset)Location
withLocationType
ofMy Region --> MySite
calledLocation B
with parentLocation A
Create Devices
Device
namednew-device1
with siteAMS01
and location set toLocation A
Device
namednew-device2
with siteAMS01
and location set toLocation B
Create Dynamic Groups
DynamicGroup
with a filter for alocation = "Location A"
nameddevices-location-a
DynamicGroup
with an empty filter as a parent group namedlocation-parent
location-parent
and set the first child group todevices-location-a
with operatorInclude (OR)
.Expected Behavior
devices-location-a
has 2 memberslocation-parent
also has 2 membersObserved Behavior
devices-location-a
has 2 memberslocation-parent
only has 1 memberWhat is happening in the workaround from #2665 is:
DynamicGroup.get_queryset()
only uses the filterset to identify membershipDynamicGroup.get_group_queryset()
instead relies on aggregated generatedQ()
objects derived from all child groupsDynamicGroup.members
was augmented to returnget_queryset()
if there are no child groups as a workaround to make filtering on tree objects likeLocation
work, as evident indevices-location-2
having 2 members fromLocation A
andLocation B
(which is a child ofLocation A
){"location": ["location-a"]}
. This will filter as expected because theTreeNodeMultipleChoiceFilter
is doing recursive descent for all child locations...Q()
objects, (e.g.location__slug='location-a'
) which you can see IS NOT recursively descending. In the case of thelocation-parent
, observe that the correct generatedQ
object should be(location__slug='location-a' OR location__slug='location-b')
.TL;DR
Q
objectsTreeNodeMultipleChoiceFilter
needs to be augmented to correctly generate a nestedQ
object for any descendants using thegenerate_query()
pattern (See:SearchFilter.generate_query()
DynamicGroup.generate_query_for_filter()
needs to be augmented to look forgenerate_query()
methods onFilter
instances when it is building nestedQ
objects for advanced dynamic group. It currently type checks if the filter is an instance ofdjango_filters.MultipleChoiceFilter
and callsget_filter_predicate()
for each value, but this isn't sufficient for tree node filters.The text was updated successfully, but these errors were encountered: