JsonResponse и FileResponse Django

Задача следующая: получил список id пользователей через Ajax функцию. На стороне сервера - данные пришли. Ответ вернул в JsonResponse.

Возникла необходимость генерировать шаблон таблицы и вставлять данные пользователей по id. После генерации хочу вызвать FileResponse и скачать в браузере сгенерированный файл.

Но этого у меня не получается. Пытался в теле функции вызвать другую функцию, которая в качестве ответа возвращает FileResponse - не срабатывает, файл не генерится и не скачивается.

Как можно выйти из данной ситуации?

p.s. формировать ссылку на скачаивание - не вариант.

Код:

@csrf_exempt
def multiblank_create(request, slug):
    local_storage = request.POST.get('persons')
    if local_storage:
        temp = json.loads(local_storage)
        ids = []
        for i in temp:
            ids.append(int(i))
            
    print('before')

    byte_io = BytesIO()
    path_to_blank = os.getcwd() + "\\main\\static\\blanks\\" + slug + "\\"
    wb = openpyxl.load_workbook(os.path.join(path_to_blank, 'create.xlsx'))
    sheet = wb['Список пользователей']
    persons = Person.objects.filter(id__in=ids)
    for item in range(1, len(persons)+1):
        sheet.insert_rows(3)
    for item in range(1, len(persons)+1):
        sheet.cell(row=item+1, column=1).value = item+1
    wb.save(byte_io)
    byte_io.seek(0)
    filename = 'Заявка '

    msg = 'ok'
    return JsonResponse(msg, safe=False)

С исправлениями:

@csrf_exempt
def multiblank_create(request, slug):
    local_storage = request.POST.get('persons')
    if local_storage:
        temp = json.loads(local_storage)
        ids = []
        for i in temp:
            ids.append(int(i))
    print('before')

    path_to_blank = os.getcwd() + "\\main\\static\\blanks\\" + slug + "\\"
    wb = openpyxl.load_workbook(os.path.join(path_to_blank, 'create.xlsx'))
    sheet = wb['Список пользователей']
    persons = Person.objects.filter(id__in=ids)
    for item in range(1, len(persons)+1):
        sheet.insert_rows(3)
    for item in range(1, len(persons)+1):
        sheet.cell(row=item+1, column=1).value = item+1
    wb.save('out_test.xlsx')
    filename = 'Заявка  '
    print('near')

    response = FileResponse(open('out_test.xlsx', 'rb'))
    response['Content-Disposition'] = 'attachment; filename='+filename+ '.xlsx'
    response['X-Sendfile'] = filename+ '.xlsx'

    return response

Если честно - у меня есть аналогичная функция, только которая генерирует файл Word и скачивается без проблем. Почему здесь такая загвоздка возникает - не могу понять.

Возможен ли вариант, что данный запрос не возможен из-за того, что данные получаю через Ajax - функцию?


Ответы (3 шт):

Автор решения: Jack_oS

Попробуйте скачать сгенерированный файл можно таким кодом во view.py:

import os
from django.http import FileResponse

def download_file(request):
    dirs = settings.TEMPLATES[0]['DIRS'][0]
    filename = 'my_file.xlsx'

    response = FileResponse(open(dirs + '/my_file.xlsx', 'rb'))
    response['Content-Disposition'] = 'attachment; filename='+filename
    response['X-Sendfile'] = filename

    return response
→ Ссылка
Автор решения: Conor McGregor

Немного поколдовав над кодом, используя подсказки, получилось следующее:

function generateBlank(url) {
    if (localStorage['persons'] != "[]" && localStorage['persons'] != undefined) {
        $.ajax({
            type: "POST",
            url: url,
            data: {
                'persons': localStorage['persons'],
            },
            success: function (data) {
                
                let a = document.createElement('a');
                a.href ="/static/blanks/test/done/" + data.path;
                a.download = data.filename;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);

            },
            error: function (data) {
                console.log('Error Server');
            },
            dataType: "json",
        });
    };
};

Функция Ajax отправляет данные в функцию Django.

@csrf_exempt
def multiblank_create(request, slug):
    local_storage = request.POST.get('persons')
    if local_storage:
        temp = json.loads(local_storage)
        ids = []
        for i in temp:
            ids.append(int(i))
    path_to_blank = os.getcwd() + "\\main\\static\\blanks\\" + slug + "\\"
    wb = openpyxl.load_workbook(os.path.join(path_to_blank, 'create.xlsx'))
    sheet = wb['Список']
    persons = Person.objects.filter(id__in=ids)
    ...
    ...
    wb.save(os.path.join(path_to_blank+'done\\', 'out_test.xlsx'))
    filename = 'Тестовая заявка.xlsx'
    data = {
        'path': 'out_test.xlsx',
        'filename': filename,
    }
return JsonResponse(data, safe=False)

На стороне сервера происходит запрос к БД. Заносятся в файл Excel данные. Сохраняются. Далее ответ отправляется в Ajax-seccess и создаеся ссылка на файл. Средсвами JS кликаем по созданной ссылке и инициализируется скачивание файла в браузер. Затем ссылка удаляется.

Код далек от идеала и не претендует на звание лучшего решения на свете, поэтому просьба - если есть что изменить или подкорректировать, то поделитесь своим мнением.

→ Ссылка
Автор решения: Роман

Передача данных из localStorage серверу (или можно получать данные со страницы):

$('#export_goods_excel_file').on('click', function () {
    let f_id = localStorage.getItem('f_id');

    window.location.href = '/goods/export_goods_excel_file/?f_id=' + f_id;
});

контроллер отвечает за фильтрацию данных, создание xl и отправку пользователю:

def export_goods_excel_file(request):
    # получаем данные, которые передали
    if request.GET.get('f_id') != '' and request.GET.get('f_id') is not None:
        id = request.GET.get('f_id')

    # фильтрация
    
    # создал xl файл
    workbook = Workbook()

    # получил первый лист и присвоил ему название
    worksheet = workbook.active
    worksheet.title = 'Goods'

    # передача данных в xl и т.п. и т.д.
   
    workbook.save(response)
    return response

Не лучшее решение, но имеет право на существование.

→ Ссылка