Refactor exam search widget: streamline filtering logic and enhance queryset efficiency
This commit is contained in:
+22
-22
@@ -6757,11 +6757,6 @@ def exam_search_widget(request):
|
||||
qobj |= Q(**{lk: q})
|
||||
qs = model.objects.filter(qobj)
|
||||
|
||||
# Limit results
|
||||
limit = 50 if q in ("*", "%") else 10
|
||||
qs = qs.order_by("name")[:limit]
|
||||
|
||||
|
||||
# Apply optional filters from GET params to the per-model queryset
|
||||
# Only include filters that were supplied in GET _and_ where the model
|
||||
# actually exposes the field. This prevents models without a given
|
||||
@@ -6777,26 +6772,31 @@ def exam_search_widget(request):
|
||||
continue
|
||||
extra_filters[pname] = val
|
||||
|
||||
# Apply filters at the queryset level for efficiency
|
||||
if extra_filters:
|
||||
try:
|
||||
qs = qs.filter(**extra_filters)
|
||||
except Exception:
|
||||
# If a filter cannot be applied at DB level, fall back to leaving qs unchanged
|
||||
pass
|
||||
|
||||
# Limit results (wildcard returns up to 50)
|
||||
limit = 50 if q in ("*", "%") else 10
|
||||
qs = qs.order_by("name")[:limit]
|
||||
|
||||
results = []
|
||||
logger.error(f"Exam search widget: model={model}, q='{q}', extra_filters={extra_filters}, initial_count={qs.count() if qs is not None else 'N/A'}")
|
||||
logger.error(f"Exam search widget: model={model}, q='{q}', extra_filters={extra_filters}, post_filter_count={qs.count() if qs is not None else 'N/A'}")
|
||||
for e in qs:
|
||||
# apply any extra_filters defensively (models may not have fields)
|
||||
skip = False
|
||||
for k, v in extra_filters.items():
|
||||
if getattr(e, k, None) != v:
|
||||
skip = True
|
||||
break
|
||||
if skip:
|
||||
if not user_can_view_exam(e, request.user):
|
||||
continue
|
||||
if user_can_view_exam(e, request.user):
|
||||
results.append({
|
||||
"id": e.pk,
|
||||
"text": str(e),
|
||||
"archive": getattr(e, "archive", False),
|
||||
"open_access": getattr(e, "open_access", False),
|
||||
"exam_mode": getattr(e, "exam_mode", False),
|
||||
"candidates_only": getattr(e, "candidates_only", False),
|
||||
})
|
||||
results.append({
|
||||
"id": e.pk,
|
||||
"text": str(e),
|
||||
"archive": getattr(e, "archive", False),
|
||||
"open_access": getattr(e, "open_access", False),
|
||||
"exam_mode": getattr(e, "exam_mode", False),
|
||||
"candidates_only": getattr(e, "candidates_only", False),
|
||||
})
|
||||
|
||||
logger.error(f"Exam search widget: returning {len(results)} results after filtering")
|
||||
|
||||
|
||||
@@ -564,17 +564,6 @@ class ExamSearchSelectMultipleWidget:
|
||||
return {{ cleaned: cleaned, filters: filters }};
|
||||
}}
|
||||
|
||||
function parseFilterTokens(q) {{
|
||||
const filters = {{}};
|
||||
if (!q) return {{ cleaned: '', filters: filters }};
|
||||
let cleaned = q.replace(tokenRe, function(m, name, val) {{
|
||||
filters[name] = /^(1|true|yes)$/i.test(val);
|
||||
return ' ';
|
||||
}});
|
||||
cleaned = cleaned.replace(/\s+/g,' ').trim();
|
||||
return {{ cleaned: cleaned, filters: filters }};
|
||||
}}
|
||||
|
||||
function fetchResults(rawQ) {{
|
||||
const parsed = parseFilterTokens(rawQ || '');
|
||||
const q = parsed.cleaned;
|
||||
|
||||
Reference in New Issue
Block a user