Fix multiple problems with search filters and query building

This commit is contained in:
Christopher Broderick 2025-03-06 02:38:42 +00:00
parent 3fab1def95
commit c30bf866df
2 changed files with 51 additions and 13 deletions

View File

@ -28,14 +28,18 @@ def query_from_base64(b64data):
def get_search_filter_args(search): def get_search_filter_args(search):
if not search:
return Q()
if search.startswith('queue:'): if search.startswith('queue:'):
return Q(queue__title__icontains=search[len('queue:'):]) return Q(queue__title__icontains=search[len('queue:'):])
if search.startswith('priority:'): if search.startswith('priority:'):
return Q(priority__icontains=search[len('priority:'):]) return Q(priority__icontains=search[len('priority:'):])
filter = Q() my_filter = Q()
for subsearch in search.split("OR"): for subsearch in search.split("OR"):
subsearch = subsearch.strip() subsearch = subsearch.strip()
filter = ( if not subsearch:
continue
my_filter = (
filter | filter |
Q(id__icontains=subsearch) | Q(id__icontains=subsearch) |
Q(title__icontains=subsearch) | Q(title__icontains=subsearch) |
@ -48,7 +52,7 @@ def get_search_filter_args(search):
Q(created__icontains=subsearch) | Q(created__icontains=subsearch) |
Q(due_date__icontains=subsearch) Q(due_date__icontains=subsearch)
) )
return filter return my_filter
DATATABLES_ORDER_COLUMN_CHOICES = Choices( DATATABLES_ORDER_COLUMN_CHOICES = Choices(
@ -91,23 +95,49 @@ class __Query__:
def __run__(self, queryset): def __run__(self, queryset):
""" """
Apply a dict-based set of filters & parameters to a queryset. Apply a dict-based set of value_filters & parameters to a queryset.
queryset is a Django queryset, eg MyModel.objects.all() or queryset is a Django queryset, eg MyModel.objects.all() or
MyModel.objects.filter(user=request.user) MyModel.objects.filter(user=request.user)
params is a dictionary that contains the following: params is a dictionary that contains the following:
filtering: A dict of Django ORM filters, eg: filtering: A dict of Django ORM value_filters, eg:
{'user__id__in': [1, 3, 103], 'title__contains': 'foo'} {'user__id__in': [1, 3, 103], 'title__contains': 'foo'}
search_string: A freetext search string search_string: A freetext search string
sorting: The name of the column to sort by sorting: The name of the column to sort by
""" """
filter = self.params.get('filtering', {}) q_args = []
filter_or = self.params.get('filtering_or', {}) value_filters = self.params.get('filtering', {})
null_filters = self.params.get('filtering_null', {})
if null_filters:
if value_filters:
# Check if any of the value value_filters are for the same field as the
# ISNULL filter so that an OR filter can be set up
matched_null_keys = []
for null_key in null_filters:
field_path = null_key[:-8] # Chop off the "__isnull"
matched_key = None
for val_key in value_filters:
if val_key.startswith(field_path):
matched_key = val_key
break
if matched_key:
# Remove the matching filters into a Q param
matched_null_keys.append(null_key)
# Create an OR query for the selected value(s) OR if the field is NULL
v = {}
v[val_key] = value_filters[val_key]
n = {}
n[null_key] = null_filters[null_key]
q_args.append((Q(**v) | Q(**n)))
del value_filters[matched_key]
# Now remove the matched mull keys
for null_key in matched_null_keys:
del null_filters[null_key]
queryset = queryset.filter( queryset = queryset.filter(
(Q(**filter) | Q(**filter_or)) & self.get_search_filter_args()) *q_args, (Q(**value_filters) & Q(**null_filters)) & self.get_search_filter_args())
sorting = self.params.get('sorting', None) sorting = self.params.get('sorting', None)
if sorting: if sorting:
sortreverse = self.params.get('sortreverse', None) sortreverse = self.params.get('sortreverse', None)

View File

@ -991,7 +991,7 @@ def ticket_list(request):
# a query, to be saved if needed: # a query, to be saved if needed:
query_params = { query_params = {
'filtering': {}, 'filtering': {},
'filtering_or': {}, 'filtering_null': {},
'sorting': None, 'sorting': None,
'sortreverse': False, 'sortreverse': False,
'search_string': '', 'search_string': '',
@ -1035,12 +1035,20 @@ def ticket_list(request):
for param, filter_command in filter_in_params: for param, filter_command in filter_in_params:
if request.GET.get(param) is not None: if request.GET.get(param) is not None:
patterns = request.GET.getlist(param) patterns = request.GET.getlist(param)
if not patterns:
continue
try:
minus_1_ndx = patterns.index("-1")
# Must have the value so remove it and configure to use OR filter on NULL
patterns.pop(minus_1_ndx)
query_params['filtering_null'][filter_null_params[param]] = True
except ValueError:
pass
if not patterns:
# Caters for the case where the filter is only a null filter
continue
try: try:
pattern_pks = [int(pattern) for pattern in patterns] pattern_pks = [int(pattern) for pattern in patterns]
if -1 in pattern_pks:
query_params['filtering_or'][filter_null_params[param]] = True
else:
query_params['filtering_or'][filter_command] = pattern_pks
query_params['filtering'][filter_command] = pattern_pks query_params['filtering'][filter_command] = pattern_pks
except ValueError: except ValueError:
pass pass