How to Save io.BytesIO pdfrw PDF into Django FileField
Asked Answered
S

2

6

What I am trying to do is basically:

  1. Get PDF from URL
  2. Modify it via pdfrw
  3. Store it in memory as a BytesIO obj
  4. Upload it into a Django FileField via Model.objects.create(form=pdf_file, name="Some name")

My issue is that when the create() method runs, it saves all of the fields except for the form.

helpers.py

import io
import tempfile
from contextlib import contextmanager

import requests
import pdfrw


@contextmanager
def as_file(url):
    with tempfile.NamedTemporaryFile(suffix='.pdf') as tfile:
        tfile.write(requests.get(url).content)
        tfile.flush()
        yield tfile.name


def write_fillable_pdf(input_pdf_path, output_pdf_path, data_dict):
    template_pdf = pdfrw.PdfReader(input_pdf_path)

    ## PDF is modified here

    buf = io.BytesIO()
    print(buf.getbuffer().nbytes). # Prints "0"!
    pdfrw.PdfWriter().write(buf, template_pdf)
    buf.seek(0)
    return buf

views.py

from django.core.files import File

class FormView(View):
    def get(self, request, *args, **kwargs):
        form_url = 'http://some-pdf-url.com'

        with as_file(form_url) as temp_form_path:
            submitted_form = write_fillable_pdf(temp_form_path, temp_form_path, {"name": "John Doe"})
            print(submitted_form.getbuffer().nbytes).  # Prints "994782"!
            FilledPDF.objects.create(form=File(submitted_form), name="Test PDF") 
        return render(request, 'index.html', {})

As you can see, print() gives out two different values as the BytesIO is populated, leading me to believe the increase in size means there is actually data in it. Is there a reason it is not saving properly into my django model instance? Also, if anyone knows a more efficient way to do this, please let me know!

Surrounding answered 11/2, 2020 at 18:49 Comment(0)
Z
11

You can use ContentFile class in your code. I did modification accordingly in your view to save your file in filefield.

from django.core.files.base import ContentFile

class FormView(View):
    def get(self, request, *args, **kwargs):
        form_url = 'http://some-pdf-url.com'

        with as_file(form_url) as temp_form_path:
            submitted_form = write_fillable_pdf(temp_form_path, temp_form_path, {"name": "John Doe"})
            pdf_content = ContentFile(submitted_form.getvalue(), 'sample.pdf')
            FilledPDF.objects.create(form=pdf_content, name="Test PDF") 
        return render(request, 'index.html', {})

You can also use the save method to store file using the ContentFile class.

from django.core.files.base import ContentFile

    class FormView(View):
        def get(self, request, *args, **kwargs):
            form_url = 'http://some-pdf-url.com'

            with as_file(form_url) as temp_form_path:
                submitted_form = write_fillable_pdf(temp_form_path, temp_form_path, {"name": "John Doe"})
                pdf_content = ContentFile(submitted_form.getvalue())
                filled_pdf = FilledPDF()
                filled_pdf.name = "Test PDF"
                filled_pdf.form.save("sample.pdf", pdf_content, save=False)
                filled_pdf.save()
            return render(request, 'index.html', {})
Zoubek answered 14/2, 2020 at 14:0 Comment(3)
Unfortunately this does not work :( My model instance saves, but the "form" field shows no uploaded filesSurrounding
@Hybrid, Forgot to add filename in ContentFile instance. Update it and also added another way to store file. This will work. :)Zoubek
Thank you! It looks like the file wasn't saving the normal way because I forgot the file name.Surrounding
B
3

Here's the documentation on how to save a file to an object.

from django.core.files import File

filled_pdf = FilledPDF()
filled_pdf.form.save('test_pdf.pdf', File(submitted_form.getvalue()), save=True)
Beeman answered 14/2, 2020 at 13:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.