Enhance exam search widget: add error handling for model retrieval and improve query parsing

This commit is contained in:
Ross
2026-02-16 12:10:00 +00:00
parent 8ba9543c89
commit 4382c6070e
+74 -8
View File
@@ -6658,7 +6658,14 @@ def exam_search_widget(request):
# Support wildcard queries # Support wildcard queries
q = q_raw q = q_raw
model = apps.get_model(model_label) try:
model = apps.get_model(model_label) if model_label else None
except Exception:
model = None
# If we couldn't resolve a model, return empty fragment rather than raising
if model is None:
return HttpResponse("")
# Helper to parse optional boolean GET params # Helper to parse optional boolean GET params
def _get_bool_param(name): def _get_bool_param(name):
@@ -6680,14 +6687,73 @@ def exam_search_widget(request):
return False return False
# Build queryset # Build queryset
qs = None qs = model.objects.none()
field_lookups = ["name__icontains"]
for lookup in field_lookups:
qs = model.objects.filter(**{lookup: q})
if qs.exists():
break
logger.error(qs) # Support field-prefixed queries like `name:term`, `code:term`, `id:123`, `date:YYYY-MM-DD`, `type:app`
field_match = re.match(r'^(?P<field>name|code|id|pk|date|type):\s*(?P<term>.+)$', q, flags=re.I)
def _field_exists(mdl, field_name):
try:
mdl._meta.get_field(field_name)
return True
except Exception:
return False
if field_match:
f = field_match.group('field').lower()
term = field_match.group('term').strip()
try:
if f in ('id', 'pk'):
qs = model.objects.filter(pk=int(term))
elif f == 'name':
qs = model.objects.filter(name__icontains=term)
elif f == 'code':
# try a few plausible code field names if present
lookups = []
for fn in ('code', 'short_code', 'identifier'):
if _field_exists(model, fn):
lookups.append(f"{fn}__icontains")
if lookups:
from django.db.models import Q
qobj = Q()
for lk in lookups:
qobj |= Q(**{lk: term})
qs = model.objects.filter(qobj)
else:
qs = model.objects.none()
elif f == 'date':
# try a few common date field names
date_lookups = []
for fn in ('date', 'exam_date', 'start_date'):
if _field_exists(model, fn):
date_lookups.append(fn)
if date_lookups:
from django.db.models import Q
qobj = Q()
for fn in date_lookups:
qobj |= Q(**{f"{fn}__startswith": term})
qs = model.objects.filter(qobj)
else:
qs = model.objects.none()
elif f == 'type':
# restrict by app_label or model name match
if term.lower() in (model._meta.app_label.lower(), model._meta.model_name.lower()):
qs = model.objects.all()
else:
qs = model.objects.none()
except Exception:
qs = model.objects.none()
else:
# Default: search name and, where available, code-like fields
field_lookups = ["name__icontains"]
for fn in ('code', 'short_code', 'identifier'):
if _field_exists(model, fn):
field_lookups.append(f"{fn}__icontains")
from django.db.models import Q
qobj = Q()
for lk in field_lookups:
qobj |= Q(**{lk: q})
qs = model.objects.filter(qobj)
# Limit results # Limit results
limit = 50 if q in ("*", "%") else 10 limit = 50 if q in ("*", "%") else 10