Pop method for Django queryset?
I am having a data model where the model contains a members field to relate to objects of the same type. the idea is that each objects can also be a group of objects. Groups can contain groups etc.
class MyObject(CommonModel):
name = models.CharField(max_length=255, unique=False, null=True, blank=True)
members = models.ManyToManyField("self", blank=True, symmetrical=False)
For a search with Django-filters I need to perform a recursive search to get all items, but also all parent group items. So I wrote this little helper function that take a query set from a previous search(by name for example) and gives back a queryset that contains all items where one of the items in the querste is in a member.
def recursive_objects_member_filter(queryset):
"""Takes a queryset and retruns a queryset of all parent objects"""
query_set_result = []
while queryset:
query_item = queryset.pop()
query_set_result.append(query_item)
members_queryset = MyObject.objects.filter(members=query_item).exclude(id =
query_item.id
)
for member in members_queryset:
queryset.append(member)
return query_set_result
My problem is that there does not seem to be a function to remove an item from a queryset like pop().
You can simplify the implementation like this (using ManyToMany functionality):
def recursive_objects_member_filter(queryset):
"""Takes a queryset and retruns a queryset of all parent objects"""
original_queryset = copy.deepcopy(queryset)
for query_item in queryset:
original_queryset |= MyObject.objects.filter(members=query_item).exclude(id =
query_item.id
)
return original_queryset
Here I am keeping the original copy of the queryset, and adding the members queryset to it in a for loop.
As I can see you don't need QuerySet
to stay QS. Just change it to list
and then use pop()
method:
def recursive_objects_member_filter(queryset):
"""Takes a queryset and retruns a queryset of all parent objects"""
queryset = list(queryset)
query_set_result = []
while queryset:
...
return query_set_result
You can try it like this:
def recursive_objects_member_filter(queryset):
"""Takes a queryset and retruns a queryset of all parent objects"""
query_set_result = []
queryset = list(queryset)
while queryset:
query_item = queryset.pop()
query_set_result.append(query_item)
members_queryset = MyObject.objects.filter(members=query_item)
queryset += list(members_queryset)
return query_set_result
Or this:
def recursive_objects_member_filter(queryset):
"""Takes a queryset and retruns a queryset of all parent objects"""
query_set_result = []
while queryset:
query_item = queryset[0]
query_set_result.append(query_item)
queryset = queryset.exclude(id=query_item.id)
members_queryset = MyObject.objects.filter(members=query_item)
queryset |= members_queryset
return query_set_result
Note that this implementation may have performance issues if the number of elements in the queryset is large, as the recursive_objects_member_filter function is inherently recursive and could result in a large number of database queries.