我有一个查询表单,可以从服务器上的数据生成一个大的json对象。理想情况下,在提交时,应将用户重定向到结果页面(+进度条),该页面将从AJAX请求更新,直到生成结果并准备好显示。
目前,当用户提交表单时,他们会在生成结果的同时将其挂在queryForm页面上。完成后,为dataFromQuery实现回调的正确方法是什么?
以下是基于类的视图的精简版:
class QueryForm(generic.View):
form_class = ReturnQuery
template_name = 'myapp/form/queryForm.html'
def get(self, request, *args, **kwargs):
#render form
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
print(request.POST)
print(form.cleaned_data)
# continues to build results object (json) which can take
# any length of time
return render(request, 'myapp/form/queryResults.html', {
'dataFromQuery': dataFromQuery,
})
我在POST方法中尝试了is_ajax():
if request.is_ajax():
results = { "progress" : {'progress':1} }
try:
dataFromQuery
except NameError:
dataFromQuery_exists = False
else:
dataFromQuery_exists = True
results['data']=dataFromQuery
return JsonResponse(results)
但是dataFromQuery一旦完成就不会传递给结果对象。
JS:
var refreshIntervalId = setInterval(function(){
$.ajax({type: "POST",url: '/website/queryForm/', data: {csrfmiddlewaretoken : csrftoken}, dataType:'json', success: function(results){ //do something with results }})
});
我已经查看了模板响应,但我不确定它们如何在这里提供帮助,因为看起来在页面渲染完成后执行回调。任何帮助将不胜感激。
编辑:我可能有点不清楚。 results.progress是在成功的AJAX上检索的,但此行:结果[&#39; data&#39;] = dataFromQuery在生成结果后不会更新。答案 0 :(得分:0)
考虑使用某些任务队列/管理器,例如celery。只需创建将产生结果的任务,以某种方式监控它并向用户显示创建响应的当前进度。
答案 1 :(得分:0)
HTTP与请求一起使用 - &gt;响应。 Django无法推动&#39;消息(如进度)到浏览器没有它询问。 您可以做的是使用轮询,每隔X秒发送一次ajax请求以获取状态。 这个解决方案创建起来并不简单,因为您需要一个单独的函数来返回进度,并在一个单独的线程中执行实现(异步)。
根据具体情况,更简单的解决方案是让您的用户知道正在处理数据,例如使用spinner。
答案 2 :(得分:0)
我目前正在开发一个非常类似的案例。你要做的不是那么微不足道。不幸的是,Django本质上是完全同步的,因此你的AJAX调用将在Python端阻塞。
如前所述,使用某种任务管理工具。我使用易于使用且完全集成在Django上的芹菜。
您的代码将如下所示
class QueryForm(generic.View):
...
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
token = generate_request_token()
if form.is_valid():
celery_task.apply_async(<token>, <parameters>) <-- this returns immediately
return render(request, 'myapp/form/queryResults.html', {
'token': <token>,
})
您的celery_task现在正在后台计算结果,并在结果完成后存储结果。 您想要存储结果的位置是您的选择。
您仍然需要创建另一个Django端点来获取结果。将其映射到类似www.yoursite.com/queryForm/data'的内容。像这样的东西
def get(self, request, *args, **kwargs):
#return data in JSON format
return JsonResponse(get_data_from_storage(request.<token>))
get_data_from_storage将查看结果存储并检查结果是准备好还是部分就绪。 (另一种方法是检查celery_task的状态。完成后,结果必须准备好)
然后在JS方面,一旦用户提交QueryForm,他就被重定向到www.yoursite.com/queryResults.html/TOKEN(结果页面),或找到将令牌传递到结果页面的方法。
您现在可以做的是使用TOKEN查询后端以查找属于该令牌的数据。进入结果页面后,您可以执行类似的操作。
var refreshIntervalId = setInterval(function(){
$.ajax({type: "GET", url: '/website/queryForm/data', data: {token: <token>}, dataType:'json', success: function(results){ if (results) {// do something} }})
});
上面的JS方法称为连续轮询,并不是最优的。 (有关替代方案,请参阅Socket.io,但您需要一个像Node.js这样的附加组件,我就是这样做的)。渲染最终在前端完成。每次获得新结果时,都会使用最新数据更新页面。
还有其他方法,但目前这对我来说似乎最直接。
芹菜的替代品是: