FileResponse или HttpResponse не возвращают pdf в качестве вложения

Я довольно новичок в Django, но я застрял на одной проблеме в течение нескольких дней. Я безуспешно пытаюсь найти ответы в Интернете.

У меня есть представление CreateReportView, в котором я пытаюсь использовать weasyprint для генерации pdf, который загружается. В будущем я хочу иметь возможность сохранять массив байтов в базе данных, чтобы пользователь мог видеть сгенерированные отчеты и затем загружать их. Но для начала нужно просто иметь возможность скачать сгенерированный pdf. При использовании метода weasyprint write_pdf() и сохранении на диск, например, в файл test.pdf, при открытии файла pdf получается нормальным. Однако, когда я пытаюсь сгенерировать PDF в переменную и сохранить его в File- или HTTPresponse как вложение, я получаю в браузере только текст с символами, который выглядит следующим образом:

%PDF-1.7 %🖤 5 0 obj <> stream xڽXm  6  _ ? 9  IpZ  [(]h   n ܧ p   r d  ή   ױg yy     ( G 1       @     \ ? z     

Я перепробовал все, что только можно придумать: менял местами Http- и FileResponse, временно сохранял файл на диске, открывал его, а затем передавал отзывчику; использовал буфер; и т.д.. Я также попробовал использовать библиотеку Reportlab и получил ту же проблему. Чтобы пояснить, когда я пытался использовать HttpResponse, я использовал следующий код:

response = HttpResponse(content_type='application/pdf;')
response['Content-Disposition'] = 'application; filename=list_equipamentos.pdf'

Я видел, что кто-то решил проблему с помощью буфера (Using Weasyprint to create file response), но у меня это не сработало. Код для этого прокомментирован в представлении ниже. Я получаю ту же проблему

Я нашел, что кто-то другой решил эту проблему, изменив процедуру 'traffic-cop', но я не могу найти код, который нужно изменить (Django - не удается загрузить файл (вместо этого он отображается как текст)).

Кроме того, при использовании режима проверки в Chrome я увидел, что ответ содержит необходимые заголовки, такие как content-disposition и content-type.

Любая помощь или руководство будут высоко оценены. Дайте мне знать, если вам нужна дополнительная информация.

Заранее спасибо!

from django.core.files import File
from django.db.models import Avg, Count, Case, When, IntegerField, DecimalField
from django.http import HttpResponse, FileResponse
from django.shortcuts import render
from django.template.loader import render_to_string
from django.views import View
from weasyprint import HTML
from salary.models import Salary

class CreateReportView(View):
    def get(self, request, *args, ** kwargs):

        ... # Omitted unrelevant code

        context = context={'results': results, 'results_per_gender': results_per_gender, 'results_per_region': results_per_region, 'results_titles': results_titles}
        rendered_html = render_to_string('report_template.html', context=context)

        # buffer = io.BytesIO()
        # pdf_file = HTML(string=rendered_html)
        # pdf_file.write_pdf(buffer)
        pdf_file = HTML(string=rendered_html).write_pdf()
        # buffer.seek(0)
        response = FileResponse(pdf_file, as_attachment=True, filename="test.pdf")
        return response

Я не упомянул, что использую HTMX на странице. На моей базовой странице я использовал hx-boost=true (htmx) в качестве атрибута в теге body. При удалении атрибута все работает, как и должно быть. Теперь я понимаю, что должен был указать эти дополнительные детали, но я не думал, что они имеют значение в данном случае. Спасибо за помощь.

weasyprint.HTML.write_pdf() возвращает экземпляр bytes при вызове без аргументов, но возвращает None при вызове с аргументами. Если вызвать экземпляр файла или str путь к файлу в качестве target, он записывает данные PDF bytes в target. Нет ничего плохого в том, что вы делали все это время на сервере.

buffer = BytesIO()
HTML(string=html).write_pdf(buffer)
buffer.seek(0)
return FileResponse(buffer, as_attachment=True, filename="test.pdf")

Проблема была полностью на стороне клиента. Когда вы добавляете атрибут HTMX hx-boost="true" к любому HTML-тегу, например, так:

<div hx-boost="true">

Что делает HTMX, так это заставляет обычные теги anchor и Form использовать AJAX. Это дает хороший эффект: если у пользователя не включен javascript, сайт будет продолжать работать.

При GET-запросе URL-адрес запроса будет помещен в стек истории браузера (если из тегов якорей). Тело ответа будет использовано для подмены innerHTML тега body страницы.

Если тело ответа является валидным HTML, браузеры могут отобразить его просто отлично. Но поскольку это не так, в этом и заключалась проблема. Браузеры не могут отобразить PDF, когда bytes из PDF используется для подмены innerHTML тега body страницы, отсюда и результат, который вы получили:

%PDF-1.7 %🖤 5 0 obj <> stream xڽXm  6  _ ? 9  IpZ  [(]h   n ܧ p   r d  ή   ױg yy     ( G 1       @     \ ? z     

Чтобы завершить ответ и позволить браузеру самостоятельно справиться с загрузкой, не пытаясь поменять местами innerHTML тега body страницы с телом ответа, добавьте атрибут hx-swap="none" к секции, участвующей в выполнении запроса:

<div hx-boost="true" hx-swap="none">
Вернуться на верх