Enhance exam search widget: add error handling for model retrieval and improve query parsing
This commit is contained in:
+74
-8
@@ -6658,7 +6658,14 @@ def exam_search_widget(request):
|
||||
# Support wildcard queries
|
||||
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
|
||||
def _get_bool_param(name):
|
||||
@@ -6680,14 +6687,73 @@ def exam_search_widget(request):
|
||||
return False
|
||||
|
||||
# Build queryset
|
||||
qs = None
|
||||
field_lookups = ["name__icontains"]
|
||||
for lookup in field_lookups:
|
||||
qs = model.objects.filter(**{lookup: q})
|
||||
if qs.exists():
|
||||
break
|
||||
qs = model.objects.none()
|
||||
|
||||
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 = 50 if q in ("*", "%") else 10
|
||||
|
||||
Reference in New Issue
Block a user