I often find myself using a ModelForm in views to display and translate views. I have no trouble displaying the form in the template. My problem is that when I am working with these, the forms often don't validate with the is_valid method. The problem is that I don't know what is causing the validation error.
Here is a basic example in views:
def submitrawtext(request):
if request.method == "POST":
form = SubmittedTextFileForm()
if form.is_valid():
form.save()
return render(request, 'upload_comlete.html')
return render(request, 'failed.html')
else:
form = SubmiittedTextFileForm()
return render(request, 'inputtest.html', {'form': form})
I know that the form is not validating because I am redirected to the failed.html template, but I never know why .is_valid is false. How can I set this up to show me the form validation errors?
Couple of things:
You are not taking the POST being sent to the POST.
To see the error message, you need to render back to the same template.
Try this:
def submitrawtext(request):
if request.method == "POST":
form = SubmittedTextFileForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'upload_comlete.html')
else:
print form.errors #To see the form errors in the console.
else:
form = SubmittedTextFileForm()
# If form is not valid, this would re-render inputtest.html with the errors in the form.
return render(request, 'inputtest.html', {'form': form})
I faced the same annoying problem and solved it by returning the form.errors.values() back with HttpResponse. Here is the code:
#csrf_exempt
def post(request):
form = UserForm(request.POST)
if form.is_valid():
return HttpResponse('All Good!')
else:
return HttpResponse(form.errors.values()) # Validation failed
In my case it returned:
<ul class="errorlist"><li>This field is required.</li></ul>
<ul class="errorlist"><li>This field is required.</li></ul>
<ul class="errorlist"><li>This field is required.</li></ul>
<ul class="errorlist"><li>This field is required.</li></ul>
It doesn't provide much information, but it is enough to give you an idea.
Related
Django version 1.8.7
I'm creating a pages where the user has to add a list of invitees to his weeding so I needed to use the same form many times on the same page to create mnay model instances of the invitees; In order to do that I used model formsets using modelformset_factory function .
So in my forms.py
class InviteesForm(ModelForm):
class Meta:
model = Invitees
fields = ['user', 'invitee_name' , 'invitee_address', 'invitee_count',
'invitee_email']
exclude = ('user',)
#creating a formset
InviteesFormSet = modelformset_factory(Invitees, form = InviteesForm )
Above you can see my model form for the invitees model and my InviteesFormSet which uses modelformser_factory fucntion.
In my views.py
def preview(request):
formset = forms.InviteesFormSet(queryset=Invitees.objects.all())
if request.method == 'POST':
formset = forms.InviteesFormSet(request.POST)
if formset.is_valid():
formset.save(commit = False)
return redirect('/invitees/')
else:
formset = forms.InviteesFormSet(request.POST)
return render(request, 'preview.html', {'formset': formset} )
In my Preview.html
<form action="/preview/" method='POST'>
{% csrf_token %}
{{ formset.management_form }}
<button type="submit">Add an Invitee</button>
</form>
The formset is not rendering in my html, am not sure what I'm doing wrong here, it's basic code! but I get this error
[u'ManagementForm data is missing or has been tampered with']
I tried the solution mentioned here by adding .management_form to the formset on template page : referred here
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#understanding-the-managementform
Can you please help me figure out the issue?
Thanks
from your code
if request.method == 'POST':
formset = forms.InviteesFormSet(request.POST)
if formset.is_valid():
formset.save(commit = False)
return redirect('/invitees/')
else:
formset = forms.InviteesFormSet(request.POST)
do not use request.POST if it is a GET request
just do
formset = forms.InviteesFormSet(queryset=Invitees.objects.all()) (you can remove the else clause altogether)
I have a simple form and whenever the user does something wrong on the form I'd like to raise a validation error on Django. The problem is that I set up the form validation but when the form is submitted with wrong values, it goes through. I was wondering why it's happening and how I can avoid that?
Here is the html form:
<form id="ask-project" method="post" action="{% url 'ask-project' %}">
{% csrf_token %}
<input required="required" class="form-control form-text required" id="prenom" name="prenom" type="text">
<button class="btn btn-default submit">Submit</button>
</form>
views.py:
def askProject(request):
if request.method == 'POST':
form = AskProjectForm(request.POST)
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
return redirect('/merci/') #success
forms.py:
class AskProjectForm(forms.ModelForm):
class Meta:
model = AskProject
fields = ['prenom']
def clean_prenom(self):
prenom = self.cleaned_data['prenom']
if len(prenom) < 3:
raise ValidationError('Votre prénom doit etre plus long que 1 caractère.')
return prenom
Am I doing something wrong?
With the pattern that you are using, this sort of problem is inevitable and order of the day. The first thing is not to render the form manually as you appear to be doing. That means you are not showing any feedback when the user enters invalid data. Consider using {{ form }}, {{ form.as_table }} etc or rendering the fields with all information as described here: https://docs.djangoproject.com/en/1.11/topics/forms/#rendering-fields-manually
Second problem is that you are redirecting when the form is submitted, regardless of whether it's valid or not. The recommended pattern is to redirect only when the form is valid. So even if you apply the suggestion in the first para, you are still not getting the required feedback. Consider implementing the form as suggested in the manual. A straight copy past follows
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
Finally getting onto the specific case of why your form validation doesn't work, add a print statement in your clean method to print out both the string and it's length see if it tallies (or if your method even gets called)
The view that **renders** is
def codequestion(request, question_id):
question = Question.objects.get(pk=question_id)
return render(request, 'polls/codequestion.html', {'question': question})
the view that is called on submission is
def codequestion_evaluate(request, question_id):
form = CodeForm()
print request.method
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
data = form.cleaned_data
return HttpResponse("Your code is %s" % data['solution'])
else:
return HttpResponse("not valid")
else:
return HttpResponse("Error")
class
from django import forms
class CodeForm(forms.Form):
solution = forms.CharField(widget=forms.Textarea)
template
<form action="{% url 'codequestion_evaluate' question.id %}" method="post">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Submit" />
</form>
I do not get the form field display in the HTML page, I can only see the submit button.
The view that is suppose to show the unfilled form doesn't create the form object at all. It should create a form object and pass it to the template, like this:
def codequestion(request, question_id):
question = Question.objects.get(pk=question_id)
form = CodeForm()
return render(request, 'polls/codequestion.html', {'question': question, 'form': form})
But better yet you should follow the pattern described in Django documentation. To do this you should:
Delete the codequestion. All actions (displaying the unfilled form, displaying a submitted form with errors, processing a correctly submitted form) will be handled by a single view.
Configure your url routing so codequestion_evaluate view handles the page showing the unfilled form.
Change codequestion_evaluate so it follows the pattern:
def codequestion_evaluate(request, question_id):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# The form has been submitted and is valid
# process the data and redirect to a "thank you" page
data = form.cleaned_data
return HttpResponseRedirect('/thanks/')
else:
# just display an empty form
form = CodeForm()
# you can optionally add 'question' if you need it in your template
question = Question.objects.get(pk=question_id)
return render(request, 'polls/codequestion.html', {'form': form, 'question': question})
form refers to a variable in your context data, since you haven't included it in the context data, it can't find it so there isn't anything to render, you need to include it.
def codequestion(request, question_id):
question = Question.objects.get(pk=question_id)
return render(request, 'polls/codequestion.html',
{'question': question, 'form': CodeForm()})
Try changing
class CodeForm(forms.Form):
to
class CodeForm(forms.ModelForm):
I faced same problem but it got resolved from this.
The recent distributions of django don't have widgets included. So:
pip install django-widgets
should do the trick.
Sorry the inconvenient, but I am extremely newbie on Django. I imagined that Django would create the forms for me if I use forms.py, I would not need to create an input tag on template, so I created a test template only, since I created the forms.py. However, running my code I was told that my view didn't return an HttpResponse object, and I suspect it was due to my template having only text on it. Could you help me creating a template to have my forms working ? We can use the example posted above. I will paste it bellow:
def create_post(request):
if request.method == 'POST':
form = CreatePostForm(request.POST)
if form.is_valid():
my_model = form.save()
return redirect('/posts/')
else:
form = CreatePostForm()
c = {'form' : form}
return render(request,'create_post.html',c)
You should return render() instead of HttpResponse:
from django.shortcuts import render
def create_a_my_model(request):
...
return render(request, 'template.html', c)
template.html can be very primitive:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button>Save</button>
</form>
Also note that the good practice is to redirect to some page after the post request. This will prevent the double submit. So the whole code of your view will be:
from django.shortcuts import redirect, render
def create_a_my_model(request):
if request.method == 'POST':
form = MyModelForm(request.POST)
if form.is_valid():
my_model = form.save()
return redirect(my_model) # if the model has `get_absolute_url()`
else:
form = MyModelForm()
return render(request, 'template.html', {'form': form})
If you model doesn't have the get_absolute_url() method the you can redirect to any other url: return redirect('/success/page/')
I have an upload form,After every form submit,i want to clear the posted data,actually the form is holding the submitted data.I know that, this problem can be solved if i redirect my page to some other page,but i don't want to redirect my page,because after submitting data,an success message will show in that page.so how can i clear my form without redirecting my page?
this is my views.py file
def UserImageUpload(request):
if request.method == 'POST':
form = DocumentForm(request.POST,request.FILES)
if form.is_valid():
messages.add_message(request, messages.SUCCESS, 'Your Image upload is waiting for Admin approval')
newdoc = Photo(photo = request.FILES['photo'],watermarked_image=request.FILES['photo'],user = request.user,name = request.POST['name'],description = request.POST['description'],keyword = request.POST['Image_Keyword'],uploaded_time=datetime.datetime.now(),Certified=request.POST['Certification'])
newdoc.save()
else:
messages.add_message(request, messages.ERROR, 'Please Complete All Fields To Submit Your Image')
else:
form = DocumentForm()
uploaded_image = Photo.objects.all()
return render_to_response('myprofile/user_image_upload.html',{'uploaded_image':uploaded_image,'form':form},context_instance = RequestContext(request))
and this is my forms.py file
from django import forms
class DocumentForm(forms.Form):
photo = forms.ImageField(
label='Select A file',)
name = forms.CharField(label='Image Name',max_length=50,widget=forms.TextInput(attrs={'class' : 'form-control',}))
Certification = forms.BooleanField(label='I certify that this is my original work and I am atlest 18 years of age')
description = forms.CharField(label='Image Description',max_length=500,widget=forms.TextInput(attrs={'class' : 'form-control',}))
Image_Keyword = forms.CharField(label='Keywords',widget=forms.TextInput(attrs={'class' : 'form-control',}))
I have solved it.In the views.py ,After saving form just assign the empty form , like that
def UserImageUpload(request):
if request.method == 'POST':
form = DocumentForm(request.POST,request.FILES)
if form.is_valid():
messages.add_message(request, messages.SUCCESS, 'Your Image upload is waiting for Admin approval')
newdoc = Photo(photo = request.FILES['photo'],watermarked_image=request.FILES['photo'],user = request.user,name = request.POST['name'],description = request.POST['description'],keyword = request.POST['Image_Keyword'],uploaded_time=datetime.datetime.now(),Certified=request.POST['Certification'])
newdoc.save()
#Assign the empty form,it will empty the form after a successful form submission
form=DocumentForm()
else:
messages.add_message(request, messages.ERROR, 'Please Complete All Fields To Submit Your Image')
else:
form = DocumentForm()
uploaded_image = Photo.objects.all()
return render_to_response('myprofile/user_image_upload.html',{'uploaded_image':uploaded_image,'form':form},context_instance = RequestContext(request))
no need to Redirect Your page.
While Rego's answer is technically correct, Django best practices dictate that you do a HttpResponseRedirect after a form submission. This reduces the likelihood of accidental multiple submissions.
You have two options for sending data to the redirected page: you could either redirect to a page that indicates the form was submitted successfully, and/or you can use session variables.
http://docs.djangoproject.com/en/stable/topics/http/sessions/
Since you can't send your view's local variables to a view that you redirect to, you can save that data in session variables that will be available to any subsequent sessions until it expires or is deleted.
For instance, you could put the user's name in a session variable in one view:
# view.py (the one with the form)
request.session['name'] = form.cleaned_data['name']
And then in the view that processes your success notice:
# view.py (form submission successful)
string = "Hi there, " + request.session['name'] + "! How are you today?"
This is preferable to not doing a redirect as strongly suggested by the Django gods.
It is strongly advised to do a redirect after a form submission to prevent accidental duplicate submissions (see Post/Redirect/Get).
You can use Django's messages framework to show messages after the redirect. In your view:
from django.contrib import messages
from django.shortcuts import redirect, render
# ...
def UserImageUpload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
# ... (do something with the submitted form data here) ...
# The message that will be shown on subsequent displays of the template:
messages.add_message(request,
messages.SUCCESS,
'Your Image upload is waiting for Admin approval')
# Redirect back to this view.
# Attention: Change '...' to the name or URL of this view.
return redirect('...')
else:
form = DocumentForm()
return render(request, 'myprofile/user_image_upload.html', {
'form': form,
'uploaded_image': Photo.objects.all(),
})
Then, you need to adjust your template (i.e. myprofile/user_image_upload.html) to be able to show the message during the next rendering of the template. Add something like this to your template:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Read the reference for more details about displaying messages in your template.