Can I make my page return with the information previously entered when the form is invalid?
Views.py
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
//do something and redirect to success page
else:
//back to the page with the information filled in by the user (HERE IS MY PROBLEM)
the line form = MyForm(request.POST or None) creates a MyForm object called form with request data assigned to it - its kinda like filled out form. The is_valid() method checks for errors in your form and adds particular errors to your form so this is now a filled out form with errors assigned. If you want to return this form to user you should add it to context so considering its a standard django function based view it should look like this:
def MyView(request):
[...code...]
if request.method == 'POST':
form = MyForm(request.POST or None)
if form.is_valid()
form.save()
return render(request, 'succes_page_template.html')
else:
return render(request, 'current_template.html', context = {'form': form})
if the form is invalid the next thing the user sees is same page where he filled out the form ('current_template.html') but with all the fields filled with data he/she already put it also form will have erros assigned to particular fields so you can print them. If you are new to Django I suggest getting into class based views from the start - they do the heavy lifting for you and refactoring + debugging becomes much easier. here is the link cheers!
Related
Ive got a large questionnaire for users to complete. Because its so big I decided to break it into three separate forms on three consecutive pages.
The urls are: questionnaire/section_1, questionnaire/section_2, questionnaire/section_3.
After submitting each form, the form data is saved to the database, and after the final (3rd) form, the three forms are saved as a single pdf for that user. Its important that users complete each of the three questionnaires.
My problem is that users will be able to use the address bar to type www.website/questionnaire/section_3 and complete just the third section, skipping the first two and submitting an incomplete questionnaire.
I cant think of any way to restrict users from accessing later parts of the form until prior parts have been successfully validated and saved.
PS - I have thought about setting permission for each of the three forms, adding permissions to the user once they have submitted one form, but I feel this is hacky??
Thank you.
This is the way I ended up dealing with this.
#allowed_users(allowed_roles=['admin', 'registered_user'])
def page_two(request):
if request.method == 'POST':
form = FormTwoForm(request.POST, request.FILES, instance=request.user.formtwo)
if form.is_valid():
form.save()
return redirect('page_three')
else:
# redirect if user didnt access the page from personal_information
if(request.META.get('HTTP_REFERER') != request.build_absolute_uri(reverse('form_one'))):
return redirect('form_one')
else:
form = FormTwoForm(instance=request.user.formtwo)
context = {
'form' : form
}
return render(request, 'example/form_two.html', context)
#allowed_users(allowed_roles=['admin', 'registered_user'])
def form_three(request):
if request.method == 'POST':
form = FormThreeForm(request.POST, request.FILES, instance=request.user.formthree)
if form.is_valid():
form.save()
return redirect('form_four')
else:
# redirect if user didnt access the page from needs_analysis
if(request.META.get('HTTP_REFERER') != request.build_absolute_uri(reverse('form_two'))):
return redirect('form_two')
form = FormThreeForm(instance=request.user.formthree)
context = {
'form' : form
}
return render(request, 'example/form_four.html', context)
After form.is_valid(), we get form.cleaned_data. How can i use this cleaned data on the next page.
For example, after the form page is processed we redirect the customer to next page, where I want to use the cleaned_data's info like name, contact, address..etc fields to be shown in next page.
def ind(request):
if request.method == 'POST':
form = form_name(request.POST)
if form.is_valid():
print(form.cleaned_data)
return render(request, 'app_one/abc.html', {'data': form.cleaned_data})
# form.save(commit=True)
# return render(request,'app_one/index.html')
else:
form=form_name()
return render(request,'app_one/index.html',{'form':form)
We will have a validated data after calling the form.is_valid() method. Once we have a validated data then we can use as we like.
For your case
customer details which are filled on the first page need show those details on the second page as receipt.
You can create a model named Reciept and save the details in the model for future reference. If you want these details in the other page views then simply pass the model object in context to render the details.
You can use the cleaned data like below
def ind(request):
if request.method == 'POST':
form = form_name(request.POST)
if form.is_valid():
context = {}.update(form.cleaned_data)
return render(request, 'app_one/abc.html', context)
# form.save(commit=True)
# return render(request,'app_one/index.html')
else:
form=form_name()
return render(request,'app_one/index.html',{'form':form)
Example Form:
class MyForm(forms.Form):
reciept_num = forms.CharField()
consider above form as an example
You can access the reciept_num data in template using the name reciept_num.
You can assign the cleaned_data to variables as usual for forms
e.g. your_data=form.cleaned_data['your_data']
After that, pass those variables to context.
e.g. context = {
'your_data':your_data
}
Lastly return the template.
e.g. return(request,'template.html',context=context)
At the 'template.html', use the variables as {{your_data}}.
I'm working on a django project where during registration, a user can submit a code to get special discounts. The validation of the discount codes is already working nicely, but I'm missing one beautifying aspect: After the user submits an invalid code I want to empty out the input field; i.e.:
def validate_code(value):
# check code for validity or raise ValidationError
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
# in my views.py
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
# modify form to return an empty html input for the code field
# this is where I'm stuck
form.fields['code'].value = ''
# ... render the form in a template
The end result should be a form with an empty input field, but the validation errors showing. The behavior should be similar to how password input fields are emptied if the form verification fails.
EDIT: I solved the problem, but in a very hacky way:
see https://stackoverflow.com/a/46564834/8572938
I'd appreciate a proper solution that does not rely on accessing protected members of the form.
the key is to reset form variable
form = CodeForm(None)
in your code
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
form = CodeForm(None)
Just render your template, if your form is not valid, it will show error, In case if it is valid process your data
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
// process your data
else:
form.data['field'] = None
return render(request, template_name, {'form': form})
Make a field validation in your form definition:
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
def clean_code(self):
code = self.cleaned_data(code)
error = # some of your process
if error:
self.fields['code'] = None
raise forms.ValidationError('...')
else:
return code
And remove the else part in your view, instead you want to do something else. If you just want to display the form with error, the raise forms.ValidationError will do it.
You can in django form add a clean_<field_name> to control each field as you like.
More info here
I found a way that works, but it's quite dirty:
old_form = CodeForm(request.POST)
form = CodeForm()
if old_form.is_valid():
# ...
else:
form._errors = old_form._errors
# pass form into the rendering context
This way, I get a clean form with the preserved errors.
While it does the job, it is clearly an ugly hack.
I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
I would like to be able to present a form to users with a dropdown list of existing records and a delete action for the selected record. So far all I can find are examples that pass the id of record to the form (such as below) and I can get this to work if I hard code the id in the function, but that's obviously not a solution. What am I missing? Thanks.
def DeleteRecord(request, id):
to_delete = get_object_or_404(MyModel, id=id)
#+some code to check if this object belongs to the logged in user
if request.method == 'POST':
form = DeleteRecordForm(request.POST, instance=to_delete)
if form.is_valid(): # checks CSRF
to_delete.delete()
return HttpResponseRedirect("/")
else:
form = DeleteRecordForm(instance=to_delete)
data = {'form': form}
return render(request, 'deleteprimerpair.html', data)