PdfFileReader.getFields() returns {} | django - python

I'm trying to read a pdf form with django. The point is that in another view of my views.py I've succeed into do it by using PyPDF2 and its PdfFileReader.getFields() method.
Now the problem is that the reading is not working properly: I've checked with adobe acrobat and the file still is a form with actually fields, so I don't really have any idea of what could be the problem.
I'm attaching here the relevant portion of the code:
if request.method == "POST":
form = Form(request.POST, request.FILES) # the form refer to a model called 'New Request'
if form.is_valid():
form.save()
File = request.FILES['File'].name
full_filename = os.path.join(BASE_DIR, 'media/media', File)
f = PdfFileReader(full_filename)
fields = f.getFields()
fdfinfo = dict((k, v.get('/V', '')) for k, v in fields.items())
k = creare_from_pdf2(request, fdfinfo, pk) # this is a custom function
nr = NewRequest.objects.all() #I'm deleting the object uploaded because it won't be useful anymore
nr.delete()
os.remove(full_filename)
If I display print(fdfinfo) it actually shows {}. This of course is leading to error when fdfinfo passes into the 'create_from_pdf_2' function. I don't really know what the problem could be, also because in another view I made exactly the same and it works:
if request.method=='POST':
form = Form(request.POST, request.FILES)
if form.is_valid():
form.save()
uploaded_filename = request.FILES['File'].name
full_filename = os.path.join(BASE_DIR, 'media/media', uploaded_filename)
f = PdfFileReader(full_filename)
fields = f.getFields()
fdfinfo = dict((k, v.get('/V', '')) for k, v in fields.items())
k=create_from_pdf1(request, fdfinfo)
if k==1:
return HttpResponse('<html><body>Something went wrong</html></body>')
nr = NewRequest.objects.all()
nr.delete()
os.remove(full_filename)
Maybe is there a way to display the errors of PdfFileReader?
UPDATING
The new file that I'm trying to reading is firstly modified in the sense that some (BUT NOT ALL!) fields are filled with PdfFileWriter, and the one filled are set then to only readable. Could this operation have influenced the performances of PdfFileReader? I'm attaching the correspondent view
att = MAIN.objects.get(id=pk)
file_path = os.path.join(BASE_DIR, 'nuova_form.pdf')
input_stream = open(file_path, "rb")
pdf_reader = PdfFileReader(input_stream, strict = False)
if "/AcroForm" in pdf_reader.trailer["/Root"]:
pdf_reader.trailer["/Root"]["/AcroForm"].update(
{NameObject("/NeedAppearances"): BooleanObject(True)})
pdf_writer = PdfFileWriter()
set_need_appearances_writer(pdf_writer)
if "/AcroForm" in pdf_writer._root_object:
# Acro form is form field, set needs appearances to fix printing issues
pdf_writer._root_object["/AcroForm"].update(
{NameObject("/NeedAppearances"): BooleanObject(True)})
data_dict1 = { # my text fields
}
data_dict2 = { # my booleancheckbox fields }
for i in range(0,6): #The pdf file has 6 pages
pdf_writer.addPage(pdf_reader.getPage(i))
page = pdf_writer.getPage(i)
# update form fields
pdf_writer.updatePageFormFieldValues(page, data_dict1)
for j in range(0, len(page['/Annots'])):
writer_annot = page['/Annots'][j].getObject()
for field in data_dict1:
if writer_annot.get('/T') == field:
writer_annot.update({
NameObject("/Ff"): NumberObject(1) # make ReadOnly
})
# update checkbox fields
updateCheckboxValues(page, data_dict2)
output_stream = BytesIO()
pdf_writer.write(output_stream)
return output_stream
def updateCheckboxValues(page, fields):
for j in range(0, len(page['/Annots'])):
writer_annot = page['/Annots'][j].getObject()
for field in fields:
if writer_annot.get('/T') == field:
writer_annot.update({
NameObject("/V"): NameObject(fields[field]),
NameObject("/AS"): NameObject(fields[field])
})

I got similar results when trying to do a straightforward read of a PDF form using Python and PyPDF2. The PDF form had been created using Libre Writer and was a single page with about 50 text fields on it. When I ran the getFields() method on the reader object I was getting the same issue -- it was returning an empty dict object.
I thought there might be a limitation on the number of fields and tried removing some for testing, but got the same result. Then when looking at it I noticed the fieldnames were all pretty long: txtLabMemberFirstName01, txtLabMemberLastName01, txtPrincipalInvestigatorFirstName, etc.
I shortened all the fields' names (e.g., "txtLMFN01") and PyPDF2 started working again as expected.

Related

How to manage Django form validation with dynamically generated forms

Let's say I have the following html page:
LIVE CODE
Let's say that in each row there is a form (I have to implement it), How can I do so that when I click on 'save' button (also to be implemented) all the inputs of each row are sent in the request.POST and I can process them individually in the backend.
This is my view for a new expense:
def new_expense(request):
data = {
'title': "New Expense",
}
data['projects'] = Project.objects.filter(is_visible=True).values('id')
data['expense_category'] = dict((y, x) for x, y in EXPENSE_CATEGORY)
data['expense_type'] = dict((y, x) for x, y in EXPENSE_TYPE)
form = ExpenseForm()
if request.method == "POST":
reset = request.POST['reset']
form = ExpenseForm(request.POST)
if form.is_valid():
form.save()
if reset == 'true':
form = ExpenseForm()
data['form'] = form
return render(request, "expense/new_expense.html", data)
I would like to create a similar view for multiple new expense creation.
One approach to this problem can be handling each row individually. You can use a very simple and effective tool called django_htmx for this task. Here is a nice article about how to handle formsets (like) rows: Build dynamic forms with Htmx. Your case seems to be a very good candidate for the example in this article.

Django for loop save instance

I have a form that has two input fields which are returning the id values that will have to be parsed and added into the database.
This is the code
if form_stage_1.is_valid() and form_stage_2.is_valid():
# GET the new TP to pass to the next instance
form_stage_1.instance.created_by = self.request.user
new_tp = form_stage_1.save()
# Parse and add suppliers to the TP
for supplier_id in request.POST["supplier"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=supplier_id)
form_stage_2.instance.side = 1
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
# Parse and add clients to the TP
for client_id in request.POST["client"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=client_id)
form_stage_2.instance.side = 2
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
messages.success(request, "TP created successfully".format())
return redirect("add-tp")
Unfortunately is only adding one client.. why the form_stage_2.instance.save() is only working once?
What would be the most appropriate way to
First of all, do you have 2 forms to fill in ?
Anyway,
for supplier_id in request.POST["supplier"]:
form_stage_2.instance.counterpart = Counterpart.objects.get(pk=supplier_id)
form_stage_2.instance.side = 1
form_stage_2.instance.save()
form_stage_2.instance.transaction_proposal.add(new_tp)
This won't work. You can't save a form instance multiple times I believe.
if form_stage_1.is_valid() and form_stage_2.is_valid():
form_stage_1.instance.created_by = self.request.user
new_tp = form_stage_1.save()
# Parse and add suppliers to the TP
for supplier_id in request.POST["supplier"]:
data = {
'counterpart':Counterpart.objects.get(pk=supplier_id),
'side':1
'transaction_proposal':[new_tp]
}
stage2_instance = Stage2ModelClass.objects.create(**data)
# Parse and add clients to the TP
for client_id in request.POST["client"]:
data = {
'counterpart':Counterpart.objects.get(pk=client_id),
'side':2
'transaction_proposal':[new_tp]
}
stage2_instance = Stage2ModelClass.objects.create(**data)
messages.success(request, "TP created successfully".format())
return redirect("add-tp")
Solution can be somewhat like this.
You can use,
for client_id in request.POST["client"]:
data = {
'counterpart':Counterpart.objects.get(pk=client_id),
'side':2,
'transaction_proposal':[new_tp]
}
form = FormClassStage2(data)
if form.is_valid():
form.save()
But I don't think you need to initialize a form here.

django display data saved from multicheckbox in template

I am trying to make an publishing option, so i use this
class Article(models.Model):
publish_options = models.CharField(max_length=50)
Now in my form, i used forms.CheckboxSelectMultiple widget. so i have this
PUBLISH_VISIBILITY = (
('All', 'All'),
('Paid-users', 'Paid-users'),
('Free Users', 'Free Users'),
('Public', 'Public'),
)
class PortalNoteForm(ModelForm):
publish_options = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=PUBLISH_VISIBILITY)
class Meta:
model = Article
Now in my view i get the values for the checkbox like this
if request.method == 'POST':
form = PortalNoteForm(request.POST)
if form.is_valid():
school_article = form.save(commit=False)
school_article.publish_options = form.cleaned_data['publish_options']
school_article.school_creator = admin
school_article.save()
return HttpResponseRedirect(reverse('going to somewhere'))
else:
form = PortalNoteForm()
context = {'form':form, 'notes':notes}
Okay this works fine saving the value of the publish_option but as a list, even if you select only one checkbox its value comes as a list. Now the problem here is i cant get to stop these publish_options value from displaying as a list in django template. I have tried iterating over them but no way. I really need help.
Well you already had a form, why do you still getting data from request.POST? Form is suppose to take request.POST and convert data into a more convenient way for you to use:
views.py
def view_func(request):
form = PortalNoteForm(request.POST or None)
if form.is_valid():
options = form.cleaned_data['public_options']
# now you have options so use it

Django - Get previous form filled data when I come back to form [duplicate]

i'm new to django so i'm sorry for my newbie question
i have a model and i need to let user edit data inside it using django forms or any other way.
look at the image above , i want to show this form ready populated with the data and let user update it.
what is the best way to do this ?
EDIT : here is my views.py code
def exam_Edit(request,examName,number=0):
numner = int(number)
number = int(number)
questionNo = int(numner)
Myexam = models.Exam.objects.get(name = examName)
QuestionsAll = models.Question.objects.filter(exam = Myexam)
myQeustion = Question.objects.filter(exam = Myexam)[nextQuestion]
answer1 = models.Asnwers.objects.filter(question=myQeustion)[0]
answer2 = models.Asnwers.objects.filter(question=myQeustion)[1]
answer3 = models.Asnwers.objects.filter(question=myQeustion)[2]
answer4 = models.Asnwers.objects.filter(question=myQeustion)[3]
# HERE IS MY PROBLEM : the line below creates a form with a data but it doesn't save it to the save object
form = QuestionsEditForm(initial = {'questionText':myQeustion.__unicode__() , 'firstChoiceText':answer1.__unicode__(),'secondChoiceText':answer2.__unicode__(),'thirdChoiceText':answer3.__unicode__(),'forthChoiceText':answer4.__unicode__()})
if request.method =='POST':
#if post
if form.is_valid():
questionText = form.cleaned_data['questionText']
Myexam = Exam.objects.get(name = examName)
myQeustion.questionText = form.cleaned_data['questionText']
answer1.answerText = form.cleaned_data['firstChoiceText']
answer1.save()
answer2.answerText = form.cleaned_data['secondChoiceText']
answer2.save()
answer3.answerText = form.cleaned_data['thirdChoiceText']
answer3.save()
answer4.answerText = form.cleaned_data['forthChoiceText']
answer4.save()
variables = RequestContext(request, {'form':form,'examName':examName,'questionNo':str(nextQuestion)})
return render_to_response('exam_edit.html',variables)
please help
Assuming you are using a ModelForm, use the instance keyword argument, and pass the model you are updating.
So, if you have MyModel and MyModelForm (the latter of which must extend django.forms.ModelForm), then your code snippet might look like:
my_record = MyModel.objects.get(id=XXX)
form = MyModelForm(instance=my_record)
And then, when the user sends back data by POST:
form = MyModelForm(request.POST, instance=my_record)
Incidentally, the documentation for ModelForm is here: http://docs.djangoproject.com/en/1.8/topics/forms/modelforms/

Populating a form in template and updating new data

I tried this, but there is no update done in django.
def update_product(request):
a= ProductForm(instance=Product.objects.get(product_id =2))#static id
render_to_response('profiles/updateproduct.html',{'form': a},RequestContext(request))
if request.method == "POST":
form = ProductForm(request.POST, instance=a)
if form.is_valid():
j=form.save(commit=False)
j.save
confirmation_message = "product information updated successfully!"
return HttpResponse("hhhh")
else:
form = ProductForm( instance = a )
You never actually call the model's save method since you are missing (). you must supply these in order to call the method.
j = form.save(commit=False)
j.save()
As a side note, since you are not doing anything to the model before saving it, you can simply replace these two lines with
j = form.save()
no real need here for the commit=False part.

Categories

Resources