Search code examples
pythondjangoreportlab

Using ReportLab To Create A PDF Then Save It To A User Model


For a few days Ive been trying to get this working and, while I think im getting close, I still havent got it working yet.

I have a function which uses ReportLab to generate a SimpleDocTemplate pdf and I want to return it to the calling view so I can save it to the uesrs Profile model.

This is my code so far:

model.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    profile_pdf = models.FileField(upload_to='pdfs/user_pdfs')

the error message:

'HttpResponse' object has no attribute 'read'

view

def final_question(request):
    if request.method == 'POST':
        # this is the final form they complete
        form = FinalQuestionForm(request.POST, request.FILES, instance=request.user.finalquestion)

        if form.is_valid():
            form.save()

            # generate pdf containing all answers to enrolment questions
            users_pdf = utils.generate_user_pdf(request)

            # here i get the users model
            # hard coded user_id for testing
            users_profile = Profile.objects.get(user_id=1)
            # then i get the field I want to save to
            pdf = users_profile.profile_pdf
            # then i save the generated pdf to the field
            pdf.save('user_1_pdf', users_pdf)

my pdf generation function

def generate_user_pdf(request):
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename = "test.pdf"'

    doc = SimpleDocTemplate(response)

    elements = []

    data= [
                 ['---TEST DATA---'],
                 ['test data'],
          ]

    [.. cont ..]

    doc.build(elements)

    return response

Thank you.

--EDIT--

error:

argument should be a bytes-like object or ASCII string, not 'FileResponse'

utils:

def generate_pdf(request):
    buffer = io.BytesIO()

    doc = SimpleDocTemplate('test')
    story = []

    doc.build(story)

    response = FileResponse(buffer.getvalue())

    return response

views.py

# generate pdf containing all answers
            user_pdf = utils.generate_pdf(request)

            # get users enrolment_pdf field
            # temp hard-coded
            user_profile = Profile.objects.get(user_id=1)
            field = user_profile.user_pdf

            # save the generated pdf to the user in the database
            file_data = ContentFile(base64.b64decode(user_pdf))
            field.save('test', file_data)

Thank you


Solution

  • Firstly, there is an answer. In addition to this answers, you need to read this, just work with buffer.getvalue() bytes array and use ContentFile class to save this to model field.

    To simplify your work with Response object, use FileResponse.

    --EDIT--

    You need to write your pdf to buffer:

    doc = SimpleDocTemplate(buffer)
    doc.build(story)
    buffer.seek(0)
    pdf: bytes = buffer.getvalue()
    return FileResponse(pdf, filename='doc.pdf')
    

    And, I've found these instructions in docs.

    To save pdf into Profile:

    file_data = ContentFile(pdf)
    profile = Profile.objects.get(...)
    profile.pdf_field.save('filename.pdf', file_data, save=False)
    profile.save()