简体   繁体   中英

Download Button Redirecting to Wrong Page

In my Django project, a user submits an Elasticsearch query into a form and it returns a downloadable report generated from that query. We've made some changes, and now I'm trying to get the portion that returns the report working again. However, I'm running into an issue with my url pattern that should call on the view function to download the report.

I have a Download Report button that appears once the report is done being generated (checked by an Ajax request). The idea is that a user will click the button, and the report will appear in their downloads folder. But, when I click the button it sends me to /report/return_doc/ instead of /return_doc/ .

The logic of sending the user to /return_doc/ is that it's associated with the return_doc function in my views, but can I trigger this function and download the report to the user without refreshing the page/sending them to a new url? Or do I need to do something entirely different to make this button functional?

error message

Page not found (404)
Request Method: GET
Request URL:    http://0.0.0.0:0001/report/return_doc/
Using the URLconf defined in audit_tool_app.urls, Django tried these URL patterns, in this order:

admin/
accounts/
form/
report/ [name='form']
report/ ^static/(?P<path>.*)$
check_progress/ [name='check_progress']
return_doc/ [name='return_doc']
[name='home']
^static/(?P<path>.*)$
The current path, report/return_doc/, didn't match any of these.

audit_tool/urls.py

from django.urls import path
from . import views
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('', views.get_query, name='form'),
]  + static(settings.STATIC_URL, document_root=settings.STAT)

audit_tool_app/urls.py

"""audit_tool_app URL Configuration"""
from django.contrib import admin
from django.urls import include, path
from django.views.generic.base import TemplateView
from django.conf import settings
from django.conf.urls.static import static
from audit_tool import views

urlpatterns = [
                  path('admin/', admin.site.urls),
                  path('accounts/', include('django.contrib.auth.urls')),
                  path('form/', include('audit_tool.urls')),
                  path('report/', include('audit_tool.urls')),
                  path('check_progress/', views.check_progress, name='check_progress'),
                  path('report/return_doc/', views.return_doc, name='return_doc'),
                  path('', TemplateView.as_view(template_name='home.html'), name='home'),
              ] + static(settings.STATIC_URL, document_root=settings.STAT)

views.py

from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from docx import Document
import os
import threading
from .forms import QueryForm
from .models import *
import time


@login_required
def get_query(request):
    if request.method == 'POST':
        form = QueryForm(request.POST)
        if form.is_valid():
            query = form.cleaned_data["query"]
            fn = "report_" + str(time.time()).replace(".", "_") + ".docx"
            t = threading.Thread(target=generate_doc, args=(query, fn))
            t.start()
            return render(request, "audit_tool/check.html", {"fn": fn})
        else:
            return HttpResponse("Your query does not appear to be valid. Please enter a valid query and try again.")
    else:
        form = QueryForm()
        return render(request, 'audit_tool/form_template.html', {'form': form})


@login_required
def check_progress(request):
    """
    Returns status of document generation
    """
    fn = request.POST["filename"]
    file = "/app/created_files/" + fn
    if not os.path.exists(file):
        return JsonResponse({"report_in_progress": 1})
    else:
        return JsonResponse({"report_in_progress": 0})


@login_required
def return_doc(request):
    """
    Returns report to user
    """
    fn = request.POST["filename"]
    file = "/app/created_files/" + fn
    doc = Document(file)
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
    response['Content-Disposition'] = 'attachment; filename={}'.format(fn)
    doc.save(response)
    return response

check.html

<!-- templates/django_audit/check.html -->
{% extends 'base_login.html' %}

{% block title %}Please wait{% endblock %}

{% load static %}

{% block content %}
<script type='text/javascript' src="{% static "bootstrap/js/jquery/1.7.1/jquery.min.js" %}"></script>
<script type="text/javascript">
$(document).ready( function() {

    var fn = $('#fn').val()
    var checkInterval = setInterval(isFileComplete, 3000); //3000 is 3 seconds


    function isFileComplete() {

        $.ajax({
        url: '/check_progress/',
        type: 'POST',
        data: {
            'filename': fn,
            'csrfmiddlewaretoken': '{{ csrf_token }}',
        },
        dataType: 'json',
        success: function (data) {
            if (data.report_in_progress == 1) {
                $("#download-button").hide();
            } else {
                $("#download-button").show();
                clearInterval(checkInterval);
            }
        }
        });
   }
   });
</script>
<p><br></p>
<p><br></p>
<div class="alert alert-primary" role="alert">
  <p>Generating {{fn}}...please wait until the Download Report button appears.</p>
  <button type="button" id="download-button" value="Download" onclick="window.open('return_doc')">Download Report</button>
</div>
<input id="fn" type=hidden value="{{fn}}">
{% endblock %}

You're making this much harder than it needs to be.

A POST is for when you want to send data to the backend, usually to update something in the database, or in the case of your get_query view to create a file. In the case of return_doc, though, you are not doing that; you are retrieving something that has already been created, namely the file. So you should continue to do as you are, which is sending a GET request.

The thing that you're not doing, though, is sending the name of the file you want to retrieve. In a GET request, that goes at the end of the URL in the query parameters - eg /mypath/?filename=myfilename . So just use that in your path:

onclick="window.open('/return_doc/?filename={{fn}}')"

and in the view:

fn = request.GET["filename"]

(Note, though, an even better solution would be to create your file in the media directory, then it can be accessed and served directly by the server without any need for the return_doc URL or view.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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