Stop spinner after Django FileResponse Save

Viewed 237

I have a template link of a url that runs a view function that generates a file and returns a FileResponse. Works great, but in cases it can take a while to generate the file so I'd like to start and stop a spinner before and after.

I've tried using a click event on the link to run and $.ajax() or $.get() function that sends the url, and in this way I can start the spinner. But the FileResponse doesn't generate a Save window in this case. (code below)

Is there a way to generate and save a file in a Django view via JavaScript? The following never opens the file save window.

$("#ds_downloads a").click(function(e){
    urly='/datasets/{{ds.id}}/augmented/'+$(this).attr('ref')
    startDownloadSpinner()
    $.ajax({
            type: 'GET',
            url: urly
        }).done(function() {
            spinner_dl.stop();
        })
  })

Adding the function below that creates the FileResponse. This generates a local system file save window allowing the user to save the file locally. The event of either opening or closing (save) that window would be the time to stop the spinner, but I can't seem to access it via javascript.

    with open(fn, 'w', newline='', encoding='utf-8') as csvfile:
      writer = csv.writer(csvfile, delimiter='\t',
               quotechar='', quoting=csv.QUOTE_NONE)
      writer.writerow(['col1','col2','col3'])
      for f in features:
        geoms = f.geoms.all()
        gobj = augGeom(geoms)
        row = [
          gobj['col1'],
          gobj['col2'],
          gobj['col3']]
        writer.writerow(row)
    response = FileResponse(open(fn, 'rb'),content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="'+os.path.basename(fn)+'"'
2 Answers

Just use HttpResponse, the browser adds the spinner automatically. No need to add extra spinner.

    from csv import writer
    from django.http import HttpResponse
    response = HttpResponse(content_type='text/csv')
    response.status_code = 200
    response["Content-Disposition"] = "attachment; filename={}".format(filename)
        try:
            csv_writer = writer(response)
            # define headers
            csv_writer.writerow(headers)
            [csv_writer.writerow(row) for row in features.values_list(*headers)]
        except Exception:
            raise
    
    return response

I came up with a somewhat klugey solution: I generate a POST request with $.ajax to a function-based view that initiates a Celery task to perform the file creation and save it to the filesystem. It returns the Celery task_id to the browser while the task runs, feeding that to a celery_progress.js routine that checks the progress of the task and on completion returns the filename; an href link is created with that in the markup, and a click() generated on it. That ajax function starts and stops a spinner with spinner.js at the appropriate points.

Not posting code b/c it is a kluge and I wouldn't recommend it. I'm guessing there's a better way to do this with a django form, but this works, the project is overdue, and I haven't got the time to look further.

Related