简体   繁体   English

django view + AJAX:如何按照POST形式检索数据

[英]django view + AJAX: how to retrieve data following form POST

I have a query form that can generate a large json object from data on the server. 我有一个查询表单,可以从服务器上的数据生成一个大的json对象。 Ideally, on submit the user should be redirected to the results page (+progress bar) that gets updated from the AJAX request until the results have been generated and are ready to display. 理想情况下,在提交时,应将用户重定向到从AJAX请求获取更新的结果页面(+进度栏),直到生成结果并准备显示为止。

Currently, when the user submits the form, they're left hanging on the queryForm page whilst the results are generated. 当前,当用户提交表单时,他们将挂在queryForm页面上,同时生成结果。 What's the correct django-way to implement a callback for dataFromQuery once it's completed? 一旦完成dataFromQuery的回调,正确的django方法是什么?

Here is a stripped down version of my class-based view: 这是基于类的视图的精简版:

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,
                })

I tried is_ajax() within the POST method: 我在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)

but dataFromQuery isn't being passed to the results object once it's complete. 但是一旦完成,就不会将dataFromQuery传递给结果对象。

JS: JS:

var refreshIntervalId = setInterval(function(){
    $.ajax({type: "POST",url: '/website/queryForm/', data: {csrfmiddlewaretoken : csrftoken}, dataType:'json', success: function(results){ //do something with results }})
});

I've looked at template responses but I'm not sure how they can help here as it looks like the callback is executed once the page render is complete. 我已经看过模板响应,但是我不确定它们如何在这里提供帮助,因为页面渲染完成后就好像执行了回调。 Any help would be appreciated. 任何帮助,将不胜感激。

EDIT: I may have been a little unclear. 编辑:我可能还不清楚。 The results.progress is retrieved on successful AJAX, but this line: results['data']=dataFromQuery isn't updating once the results have been generated. 在成功的AJAX上会检索到results.progress,但是这一行:result ['data'] = dataFromQuery一旦生成结果就不会更新。

Consider using some task queue/manager, like celery . 考虑使用一些任务队列/管理器,例如celery Just create task that will produce your result, monitor it somehow and display to user current progress of creating response. 只需创建将产生结果的任务,以某种方式对其进行监视并向用户显示创建响应的当前进度即可。

HTTP works with request -> response. HTTP与请求->响应一起使用。 Django can't 'push' messages (like progress) to the browser without it asking. Django无法在没有询问的情况下将消息(例如进度)“推送”到浏览器。 What you could do is use polling, which is sending an ajax request every X seconds to get the status. 您可以做的是使用轮询,即每X秒发送一个ajax请求以获取状态。 This solution is not that simple to create, as you'll need a seperate function that returns the progress, and do the actualling progressing in a seperate thread (asynchronous). 创建此解决方案并非那么简单,因为您需要一个单独的函数来返回进度,并在单独的线程(异步)中进行实际的进度。

depending on the situation, a much easier solution is let your user know that data is being processed, by using a spinner for example. 根据具体情况,一个更简单的解决方案是使您的用户知道数据正在处理,例如使用微调器

I am currently developing a very similar case. 我目前正在开发一个非常相似的案例。 What you are trying to do is not so trivial. 您尝试做的并不是那么琐碎。 Unfortunately Django is totally synchronous by nature, therefor your AJAX calls will be blocking on the Python side. 不幸的是,Django本质上是完全同步的,因此您的AJAX调用将在Python方面受阻。

As already suggested use some kind of task management tool. 如已建议使用某种任务管理工具。 I use celery which is easy to use and fully integrated on Django. 我使用的celery易于使用,并且完全集成在Django上。

Your code will look like this 您的代码将如下所示

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>,
                })

Your celery_task is now calculating the results in the background and will store the results once it has finished. 您的celery_task现在正在后台计算结果,完成后将存储结果。 Where you want to store the results is your choice. 您希望将结果存储在何处。

You still need to create another Django endpoint to get the result. 您仍然需要创建另一个Django端点来获取结果。 Map it to something like www.yoursite.com/queryForm/data'. 将其映射到类似www.yoursite.com/queryForm/data的文件。 Something like this 像这样

def get(self, request, *args, **kwargs):
    #return data in JSON format
    return JsonResponse(get_data_from_storage(request.<token>))

get_data_from_storage will look into the result storage and check if the results are ready or partially ready. get_data_from_storage将调查结果存储并检查结果是否已准备好或部分准备好。 (Another approach is to check the status of the celery_task. When it is done, the results must be ready) (另一种方法是检查celery_task的状态。完成后,必须准备好结果)

Then on the JS side, as soon as the user submit the QueryForm, he is redirected to www.yoursite.com/queryResults.html/TOKEN (result page), or find a way to pass the token to the result page. 然后在JS端,一旦用户提交QueryForm,他就被重定向到www.yoursite.com/queryResults.html/TOKEN(结果页面),或找到一种将令牌传递到结果页面的方法。

What you can do now is to use TOKEN to query the backend for data belonging to that token. 现在,您可以使用令牌来查询后端以获取属于该令牌的数据。 Once inside the result page you do something like. 进入结果页面后,您可以执行类似操作。

var refreshIntervalId = setInterval(function(){
    $.ajax({type: "GET", url: '/website/queryForm/data', data: {token: <token>}, dataType:'json', success: function(results){ if (results) {// do something} }})
});

The JS approach above is called continuous polling and it is not optimal. 上面的JS方法称为连续轮询,它不是最佳方法。 (See Socket.io for alternative, but you will need an additional component like Node.js, I went this way). (有关替代方法,请参见Socket.io,但是您将需要诸如Node.js之类的其他组件,我就是这样)。 The rendering is finally done on the frontend side. 渲染最终在前端完成。 Each time you get new results, you update the page with fresh data. 每次获得新结果时,都使用新数据更新页面。

There are other approaches, but currently this seemed the most straightforward to me. 还有其他方法,但是目前对我来说这似乎是最简单的方法。

Alternatives to Celery are: 芹菜的替代品是:

  • Threads (if u know what you are doing) 线程(如果您知道您在做什么)
  • RabbitMQ/ZeroMQ RabbitMQ /零MQ
  • Tornado (but might not be easy to integrate with Django) 龙卷风(但可能不容易与Django集成)
  • Gevent Gevent

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM