Объединение двух представлений для создания PDF в Django - PullRequest
1 голос
/ 04 августа 2020

Новым читателям я был бы очень признателен, если бы вы предложили, как я могу улучшить свой вопрос. Спасибо!

У меня есть два рабочих представления для создания PDF. Я хотел бы создать представление, которое объединяет эти два представления и объединяет выходные PDF-файлы, созданные двумя представлениями, для создания одного PDF-файла, содержащего выходные PDF-файлы двух представлений. Я также хотел бы указать, что эти два представления используют разные подходы для создания PDF-файлов. Первый отображает шаблон HTML для создания PDF-файла, а второй создает PDF-файл с нуля с помощью Reportlab. Я новичок в Django, как я могу это сделать?

Просмотр 1

class GenerateAllDocs(View):
    def allDocGen(request):
        if request.method == 'POST':
            all_doc_gen_form = GenerateAllForms(request.POST)
            if all_doc_gen_form.is_valid():

                some_Field_1= all_doc_gen_form.cleaned_data['some_Field_1']
                some_Field_2= all_doc_gen_form.cleaned_data['some_Field_2']


                template = get_template('PDF_templates/att_pg_pdf_template.html')

                data = {
                    'some_Field_1': some_Field_1,
                    'some_Field_2': some_Field_2,

                }

                html = template.render(data)
                pdf = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)
                if pdf:
                    response = HttpResponse(pdf, content_type = 'application/pdf')
                    filename = "something - %s.pdf" %(data.get('zzzzz'))
                    content = "inline; filename=%s" %(filename)
                    download = request.GET.get('download')
                    if download:
                        content = "attachment; filename%s" %(filename)
                    response['Content-Disposition'] = content
                    return response
                return HttpResponse('Not Found')


        all_doc_gen_form = GenerateAllForms()
        return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})

Просмотр 2

def template_PDF_view(request):

    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

    buffer = BytesIO()

    # Create the PDF object, using the BytesIO object as its "file."
    p = canvas.Canvas(buffer, pagesize=letter)

    # Draw things on the PDF. Here's where the PDF generation happens.


    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)

    p.drawString(40, 724, "  ".join('XX-XXX-XX'.replace('-','').upper())) # car_licence_plate
    p.drawString(193, 149, 'hello 123') # address_city      

    # Close the PDF object cleanly.
    p.showPage()
    p.save()

    # Get the value of the BytesIO buffer and write it to the response.
    pdf = buffer.getvalue()
    buffer.close()
    response.write(pdf)
    return response

РЕДАКТИРОВАТЬ # 1 Следуя предложению ktowen , вот мой обновленный код. Я создал представление на основе функций для своего представления 2 и вызвал эту функцию внутри представления 1, а затем попытался объединить два PDF-файла, созданных представлениями. Это по-прежнему не работает - сообщение об ошибке пишется после кода.

def create_cerfa(request):
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
    buffer_1 = BytesIO()

    p = canvas.Canvas(buffer_1)
    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)
    p.drawString(40, 724, "  ".join('AZ-343-BT'.replace('-','').upper()))
    p.drawString(178, 724, "  ".join('VF77JNFUC9J177958').upper())
    p.save()
    
    pdf = buffer_1.getvalue()
    buffer_1.close()
    response.write(pdf)
    return response

def createAttestation(request):
    if request.method == 'POST':
        all_doc_gen_form = GenerateAllForms(request.POST)
        if all_doc_gen_form.is_valid():

            data1 = all_doc_gen_form.cleaned_data['data1']
            data12 = all_doc_gen_form.cleaned_data['data12']

            template = get_template('PDF_templates/att_pg_pdf_template.html')

            data = {
                'data1': data1,
                'data12': data12,

            }

            html = template.render(data)
            pdf = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)

            cerfa = create_cerfa(request)

            pdf1resp = HttpResponse(pdf, content_type = 'application/pdf')
            pdf2resp = HttpResponse(cerfa, content_type = 'application/pdf')

            pdfs = [pdf1resp, pdf2resp]

            merger = PdfFileMerger()

            for item in pdfs:
                merger.append(item)

            merger.write()
    
            if pdf:
                response = HttpResponse(merger, content_type = 'application/pdf')
                return response

    all_doc_gen_form = GenerateAllForms()
    return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})

Сообщение об ошибке

Traceback (most recent call last):
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\worrk\PyDjango\mysite - PROD VERSION\main\views.py", line 460, in createAttestation
    merger.append(item)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PyPDF2\merger.py", line 203, in append
    self.merge(len(self.pages), fileobj, bookmark, pages, import_bookmarks)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PyPDF2\merger.py", line 133, in merge
    pdfr = PdfFileReader(fileobj, strict=self.strict)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PyPDF2\pdf.py", line 1084, in __init__
    self.read(stream)
  File "C:\Users\Work\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PyPDF2\pdf.py", line 1689, in read
    stream.seek(-1, 2)

Exception Type: AttributeError at /tst2/
Exception Value: 'HttpResponse' object has no attribute 'seek'

EDIT # 2

Итак, я сузил проблему до фактического кода слияния PDF-файлов. Я протестировал подход, приведенный в EDIT 2 by ktowen.

Я прокомментировал часть слияния, а затем вернул PDF-файлы следующим образом:

return FileResponse(pdf1, as_attachment=True, content_type='application/pdf')

Я проверил это с помощью PDF1 и PDF2 - оба генерируются, но я не понимаю, что не так с частью слияния? и как это исправить?

Незначительное редактирование - # 2.1 Вот моя функция рендеринга в PDF

from __future__ import print_function
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
    
def render_to_pdf(template_src, context_dict={}):
        template = get_template(template_src)
        html  = template.render(context_dict)
        result = BytesIO()
        pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
        if not pdf.err:
            return HttpResponse(result.getvalue(), content_type='application/pdf')
        return None

EDIT # 3 Здесь я изменил определение PDF внутри функции render_to_pdf следующим образом.

pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
pdf = result.getvalue()

Теперь ошибка 'utf-8' codec can't decode byte 0x93 in position 10: invalid start byte

Поэтому, чтобы устранить эту ошибку, я попытался изменить кодировку внутри функции render_to_pdf . Поскольку я много использую французский язык в этом проекте, я протестировал рекомендуемые кодировки, такие как latin1 , UTF-8 и значение по умолчанию ISO-8859-1 , но все равно получаю та же ошибка.

Ниже приводится полное сообщение об ошибке

Traceback (most recent call last):
  File "C:\Users\Work\AppData\Roaming\Python\Python38\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Work\AppData\Roaming\Python\Python38\site-packages\django\core\handlers\base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\worrk\PyDjango\mysite - PROD VERSION\main\views.py", line 596, in createAttestation
    pdf1 = PdfFileReader(open(p1,'rb'))

Exception Type: UnicodeDecodeError at /tst2/
Exception Value: 'utf-8' codec can't decode byte 0x93 in position 10: invalid start byte

Ответы [ 2 ]

0 голосов
/ 07 сентября 2020

Итак, я решил это довольно долго go ...

В подходе 1 я использовал шаблон HTML и заполнял данные из формы в нем и конвертировал их в PDF, и в подходе 2 я использовал reportlab. Поэтому я просто переписал подход 1 с помощью reportlab - слияния не требуется. метод .showpage создает разрывы страниц.

0 голосов
/ 04 августа 2020

Вы можете извлечь logi c генерации PDF в две функции и использовать PyPDF2 PdfFileMerger для объединения двух PDF-файлов.

Это общая идея:

from PyPDF2 import PdfFileMerger

def merged_PDF_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

    pdf1_buffer = get_pdf1()
    pdf2_buffer = get_pdf2()

    pdf_merger = PdfFileMerger()
    pdf_merger.append(pdf1_buffer)
    pdf_merger.append(pdf2_buffer)

    # This can probably be improved 
    pdf_merger.write(buffer)
    pdf_merger.close()
    buffer.seek(0)

    response.write(buffer.getvalue())
    return response
    

РЕДАКТИРОВАТЬ 1 Попробуйте с этим

class WillThisWork(View): # I hope so :|
    def merged_PDF(request):

        buffer = BytesIO()

        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

        pdf1_buffer = createAttestation(request)
        pdf2_buffer = create_cerfa(request)

        pdf_merger = PdfFileMerger()
        pdf_merger.append(pdf1_buffer)
        pdf_merger.append(pdf2_buffer)

        pdf_merger.write(buffer)
        pdf_merger.close()
        buffer.seek(0)

        return FileResponse(buffer, as_attachment=True, filename='hello.pdf')

EDIT 2 На основе вашего редактирования попробуйте это

def create_cerfa_pdf():
    filestream = BytesIO()

    p = canvas.Canvas(filestream)
    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)
    p.drawString(40, 724, "  ".join('AZ-343-BT'.replace('-','').upper()))
    p.drawString(178, 724, "  ".join('VF77JNFUC9J177958').upper())
    p.save()
    
    filestream.seek(0)
    return filestream

def create_cerfa(request):
    pdf = create_cerfa_pdf()
    return FileResponse(pdf, as_attachment=True, filename="somefilename.pdf")

def createAttestation(request):
    if request.method == 'POST':
        all_doc_gen_form = GenerateAllForms(request.POST)
        if all_doc_gen_form.is_valid():

            data1 = all_doc_gen_form.cleaned_data['data1']
            data12 = all_doc_gen_form.cleaned_data['data12']
            template = get_template('PDF_templates/att_pg_pdf_template.html')
            data = {
                'data1': data1,
                'data12': data12,
            }
            html = template.render(data)

            pdf1 = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)
            pdf2 = create_cerfa_pdf()

            merger = PdfFileMerger()
            for item in [pdf1, pdf2]:
                merger.append(item)

            filestream = BytesIO()
            merger.write(filestream)
            merger.close()
            filestream.seek(0)
    
            return FileResponse(filestream, as_attachment=True)

    all_doc_gen_form = GenerateAllForms()
    return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})
...