Django中基于表单字段值动态拉取API数据的医疗设施搜索问题
解决方案:Django医疗设施搜索的动态结果过滤
嘿,我之前在Django项目里做过类似的医疗设施层级搜索功能,刚好能给你一些实用的解决方案!根据你的需求,我们可以分两种场景来实现「仅拉取相关结果」的功能——层级联动过滤(国家→地区→城市→医疗设施)和实时关键词搜索过滤,下面分别详细说明:
一、层级联动过滤(适合按地理层级逐步缩小范围)
这种方案适合用户通过选择上级选项(比如国家),自动加载对应的下级选项(比如该国家的地区),避免一次性加载全部数据,提升页面性能和用户体验。
1. 后端视图编写
首先创建几个API视图,用于根据父级ID返回对应的子级数据:
from django.http import JsonResponse from django.views.decorators.cache import cache_page from .models import Country, Region, City, MedicalFacility # 缓存地区数据(24小时,因为地理数据不会频繁更新) @cache_page(60 * 60 * 24) def get_regions(request): country_id = request.GET.get("country_id") if not country_id: return JsonResponse([], safe=False) # 过滤对应国家的地区,返回ID和名称 regions = Region.objects.filter(country_id=country_id).values("id", "name") return JsonResponse(list(regions), safe=False) # 同理编写获取城市的视图 @cache_page(60 * 60 * 24) def get_cities(request): region_id = request.GET.get("region_id") if not region_id: return JsonResponse([], safe=False) cities = City.objects.filter(region_id=region_id).values("id", "name") return JsonResponse(list(cities), safe=False) # 获取对应城市的医疗设施 def get_facilities(request): city_id = request.GET.get("city_id") if not city_id: return JsonResponse([], safe=False) facilities = MedicalFacility.objects.filter(city_id=city_id).values("id", "name", "address") return JsonResponse(list(facilities), safe=False)
然后在urls.py中配置这些API路由:
from django.urls import path from . import views urlpatterns = [ # 其他路由... path("api/get-regions/", views.get_regions, name="get_regions"), path("api/get-cities/", views.get_cities, name="get_cities"), path("api/get-facilities/", views.get_facilities, name="get_facilities"), ]
2. 前端交互实现
在模板中添加选择框,并通过JavaScript监听选择事件,发送AJAX请求更新下级选项:
<form method="get" action="{% url 'search_results' %}"> <div class="form-group"> <label>国家:</label> <select id="country-select" name="country"> <option value="">请选择国家</option> {% for country in countries %} <option value="{{ country.id }}">{{ country.name }}</option> {% endfor %} </select> </div> <div class="form-group"> <label>地区:</label> <select id="region-select" name="region" disabled> <option value="">请先选择国家</option> </select> </div> <div class="form-group"> <label>城市:</label> <select id="city-select" name="city" disabled> <option value="">请先选择地区</option> </select> </div> <div class="form-group"> <label>医疗设施:</label> <select id="facility-select" name="facility" disabled> <option value="">请先选择城市</option> </select> </div> <button type="submit" class="btn btn-primary">搜索</button> </form> <script> document.addEventListener('DOMContentLoaded', () => { const countrySel = document.getElementById('country-select'); const regionSel = document.getElementById('region-select'); const citySel = document.getElementById('city-select'); const facilitySel = document.getElementById('facility-select'); // 国家选择变化时更新地区 countrySel.addEventListener('change', async () => { const countryId = countrySel.value; regionSel.disabled = !countryId; citySel.disabled = true; facilitySel.disabled = true; // 清空下级选项 regionSel.innerHTML = '<option value="">请选择地区</option>'; citySel.innerHTML = '<option value="">请先选择地区</option>'; facilitySel.innerHTML = '<option value="">请先选择城市</option>'; if (!countryId) return; try { const response = await fetch(`/api/get-regions/?country_id=${countryId}`); const regions = await response.json(); let options = '<option value="">请选择地区</option>'; regions.forEach(region => { options += `<option value="${region.id}">${region.name}</option>`; }); regionSel.innerHTML = options; } catch (err) { console.error('加载地区失败:', err); } }); // 地区选择变化时更新城市 regionSel.addEventListener('change', async () => { const regionId = regionSel.value; citySel.disabled = !regionId; facilitySel.disabled = true; citySel.innerHTML = '<option value="">请选择城市</option>'; facilitySel.innerHTML = '<option value="">请先选择城市</option>'; if (!regionId) return; try { const response = await fetch(`/api/get-cities/?region_id=${regionId}`); const cities = await response.json(); let options = '<option value="">请选择城市</option>'; cities.forEach(city => { options += `<option value="${city.id}">${city.name}</option>`; }); citySel.innerHTML = options; } catch (err) { console.error('加载城市失败:', err); } }); // 城市选择变化时更新医疗设施 citySel.addEventListener('change', async () => { const cityId = citySel.value; facilitySel.disabled = !cityId; facilitySel.innerHTML = '<option value="">请选择医疗设施</option>'; if (!cityId) return; try { const response = await fetch(`/api/get-facilities/?city_id=${cityId}`); const facilities = await response.json(); let options = '<option value="">请选择医疗设施</option>'; facilities.forEach(facility => { options += `<option value="${facility.id}">${facility.name} - ${facility.address}</option>`; }); facilitySel.innerHTML = options; } catch (err) { console.error('加载医疗设施失败:', err); } }); }); </script>
二、实时关键词搜索过滤(适合用户直接输入关键词缩小范围)
如果需要支持用户输入关键词实时过滤医疗设施,可以结合层级过滤和关键词搜索,实现更灵活的搜索体验。
1. 后端搜索视图
from django.http import JsonResponse from .models import MedicalFacility def search_facilities(request): # 获取前端传入的参数 query = request.GET.get("q", "").strip() country_id = request.GET.get("country_id", "") region_id = request.GET.get("region_id", "") city_id = request.GET.get("city_id", "") # 基础查询集 facilities = MedicalFacility.objects.all() # 按关键词过滤(模糊匹配名称或地址) if query: facilities = facilities.filter( name__icontains=query ) | facilities.filter( address__icontains=query ) # 按地理层级过滤 if country_id: facilities = facilities.filter(city__region__country_id=country_id) if region_id: facilities = facilities.filter(city__region_id=region_id) if city_id: facilities = facilities.filter(city_id=city_id) # 返回结果(限制数量避免数据过多) return JsonResponse(list(facilities.values("id", "name", "address")[:50]), safe=False)
在urls.py添加路由:
path("api/search-facilities/", views.search_facilities, name="search_facilities"),
2. 前端搜索交互
在模板中添加搜索框,并监听输入事件:
<div class="form-group"> <label>搜索医疗设施:</label> <input type="text" id="search-input" placeholder="输入名称或地址搜索..." class="form-control"> </div> <script> const searchInput = document.getElementById('search-input'); const facilitySel = document.getElementById('facility-select'); searchInput.addEventListener('input', async () => { const query = searchInput.value; const countryId = countrySel.value; const regionId = regionSel.value; const cityId = citySel.value; // 构建请求URL let urlParams = new URLSearchParams(); if (query) urlParams.append('q', query); if (countryId) urlParams.append('country_id', countryId); if (regionId) urlParams.append('region_id', regionId); if (cityId) urlParams.append('city_id', cityId); try { const response = await fetch(`/api/search-facilities/?${urlParams.toString()}`); const facilities = await response.json(); let options = '<option value="">请选择医疗设施</option>'; facilities.forEach(facility => { options += `<option value="${facility.id}">${facility.name} - ${facility.address}</option>`; }); facilitySel.innerHTML = options; } catch (err) { console.error('搜索失败:', err); } }); </script>
三、优化建议
- 缓存优化:对于地理数据(国家、地区、城市),使用Django的缓存装饰器
@cache_page减少数据库查询,提升响应速度。 - 前端体验:添加加载动画(比如请求时在选择框旁显示加载图标),避免用户重复操作。
- 参数验证:后端视图中可以添加参数类型验证(比如检查
country_id是否为整数),避免非法请求。 - 分页处理:如果医疗设施数量极大,在搜索视图中添加分页,避免一次性返回过多数据。
内容的提问来源于stack exchange,提问作者kbdev
相关产品推荐
相关产品推荐

