In views.py:
if form.is_valid():
# ...process form...
return HttpResponseRedirect(reverse('auctions:index'))
else:
return HttpResponseRedirect(reverse('auctions:create'))
In the template, doing {{ form.fieldname }} renders the form field correctly. However, if I submit a value for this field that passes html validation but doesn't pass is_valid(), the error message doesn't render in the template even though I put {{ form.fieldname.errors }}. What am I doing wrong?
Edit: I realized I am just rendering an unbound form with the HttpResponseRedirect so I changed my code to this:
if form.is_valid():
#...process form...
return HttpResponseRedirect(reverse('auctions:index'))
else:
return render(request, "auctions/create.html", {'form': form})
And this works, however, it's not the best practice to use render() instead of HttpResponseRedirect() after a user submits a form right? Any way to get around this?
Related
I need a call a form on an HTML template where the user posts data which saves to model
The code is running without any errors
But the html page display only title and button
No text input fields
I have a form which is to be displayed on a html page so the user can input data and it saves the data into the model.I am not getting any errors while executing th code but the template does not display the form it just shows the title and submit button
def boqmodel1(request):
form = boqform(request.POST)
if form.is_valid():
obj=form.save(commit=False)
obj.save()
context = {'form': form}
return render(request, 'create.html', context)
else:
context = {'error': 'The post has been successfully created.
Please enter boq'}
return render(request, 'create.html', context)
MyTemplate
<form action="" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Create boq"/>
</form>
MY Url
urlpatterns = [
url(r'^create/', views.boqmodel1, name='boqmodel1'),
path('', views.boq, name='boq'),
]
First of all, your first request, without submitting form is GET. When you submit a form you send POST.
The form is not displaying, because your form is not valid in the first place. Your function should look like this:
def boqmodel1(request):
context = {}
if request.method == "GET":
form = boqform()
context["form"] = form
# if you post a form do all the saving
if request.method == "POST":
form = boqform(request.POST)
context = {'form': form}
if form.is_valid():
obj=form.save()
return render(request, 'create.html', context)
else:
context["errors"] = form.errors
return render(request, 'create.html', context)
If method is GET init your form and pass it to your context, so you can display it on frontend.
If method is POST, init your form with your data from frontend (request.POST), check if the form is valid. If it is valid - save it. If it is not valid, return your errors and display them as you wish.
I have got a modal contact form. The form works fine, but the page reload resubmits previously entered values.
view.py
...
if request.POST:
form = ContactForm(request.POST)
if form.is_valid():
entry = form.save()
form = ContactForm()
c.update(locals())
return render(request, 'template.html', c )
...
template.html
<form action="." method="post" id="contact-form">
...
</form>
I tried to clear the form in the GET request. Form fields are cleared, but this does not prevent submission on the page reload.
else: #request.GET
form = ContactForm()
c.update(locals())
return render(request, 'template.html', c )
I have seen many examples suggesting a redirect. I do understand this approach, but in case of a modal form I would like to avoid a redirect.
How to prevent modal form re-submissions? Is the redirect really the only way?
Solution seems very simple:
Replaced this line...
return render(request, 'template.html', c )
... with the one below:
return HttpResponseRedirect(request.path_info)
I am trying to render a Django contact form on any arbitrary page. I am doing it with a request context processor and a template include. This allows me to display the form fine anywhere I want. Then I have a special URL that accepts POST requests (on GET, I just redirect them). If the form is valid, I send an email, and redirect to a success page. On form invalid, I know to pass the form bound with errors, but...I don't know which template to specify because the form is an include and the parent template could be anywhere.
The only way to get something in a Django view is from the request. I can get the path, and with more work, probably the original view from where the POST came from, but that doesn't get me the template.
# urls.py
url(r'^services/$', 'website.views.services', name='services'),
url(r'^services/contact/$', 'website.views.services_contact', name='services_contact'),
url(r'^services/contact/done/$', 'website.views.services_contact_done', name='services_contact_done')
# views.py
class ServicesView(TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
class ServicesContactView(View):
def get(self, request, *args, **kwargs):
return redirect('services')
def post(self, request, *args, **kwargs):
form = ContactForm(request.POST)
if form.is_valid():
form.send_email()
return redirect('services_contact_done')
else:
return render(request, ????, {'contact_form': form})
services_contact = ServicesContactView.as_view()
# contact.html
<h2>Contact me</h2>
<p>Enter your email to receive your questionnaire</p>
<form action="{% url 'services_contact' %}" method="post">
{% csrf_token %}
{% if contact_form.non_field_errors %}
{{ contact_form.non_field_errors }}
{% endif %}
{{ contact_form.as_p }}
<button type="submit" name="submit">Send questionnaire</button>
</form>
# home.html
{% extends "base.html" %}
{% block content %}
<h1>{{ site.name }}</h1>
{% include "services/contact.html" %}
{% endblock %}
The typical Django form view is somewhat silent on form invalid in that its scenario is mostly similar to an unbound form, so it's all just render in the end. My scenario is different due to the template include.
You could set up a session variable every time you render a template and use it afterwards when you need it :
request.session['template']="nameOfTemplate"
.
return render(request, request.session.get('template', 'default.html'), {'contact_form': form})
I know it requires to write a line of code every time you render a template, but that's the best solution I could think of.
If anybody needs this answer, I figured it out on my own. It's possible, but a different approach is required. First, a request context processor is not appropriate for this situation. They're fairly dumb because they just get something once and stick it in the context. Their only advantage is their global nature.
My context processor:
def contact_form(request):
"""
Gets the contact form and adds it to the request context.
You almost certainly don't want to do this.
"""
form = ContactForm()
return {'contact_form': form}
The nature of forms is that they act differently after being processed by Django's validation machinery, specifically ContactForm() is an unbound form and will always be. You don't want to do this (unless you want a form that simply displays but doesn't work). The TEMPLATE_CONTEXT_PROCESSORS should be edited to remove this processor.
Now the burden on displaying the form is back on the view, which also means just about any view must be able to handle POST requests as well. This means that editing each view that wants a contact form is required, but we can use the power of class-based views and mixins to handle most of the repetition.
ServicesView remains almost the same as a TemplateView, except with a mixin that will handle the form. This way, the template name always remains the same (my original problem), but with additional form power.
class ServicesView(ContactMixin, TemplateView):
template_name = 'services/services.html'
services = ServicesView.as_view()
ContactMixin uses FormMixin, to create and display a form, and ProcessFormView to handle the GET and POST requests for the form. And because the form's nature changes with different kinds of requests (unsubmitted, submitted and invalid, submitted and valid), get_context_data needs to be updated with the correct form class instance. Lastly, we probably want to prefix (namespace) our form because it can can be used anywhere, and we want to avoid conflicts when another possible form can POST to the same view. Thus, the mixin is:
class ContactMixin(FormMixin, ProcessFormView):
form_class = ContactForm
success_url = reverse_lazy('contact_done')
def get_form_kwargs(self):
kwargs = super(ContactMixin, self).get_form_kwargs()
kwargs['prefix'] = 'contact'
return kwargs
def get_context_data(self, **kwargs):
context = super(ContactMixin, self).get_context_data(**kwargs)
form_class = self.get_form_class()
context['contact_form'] = self.get_form(form_class)
return context
def form_valid(self, form):
form.send_email()
return super(ContactMixin, self).form_valid(form)
The subtleties of self.get_form_class() were almost lost on me if it were not for an example in the docs (of what not to do, heh) and another StackOverflow answer, where I would've usually just said self.form_class, which ignores the processing of the form.
Now I simply add ContactMixin to any view and {% include "includes/contact.html" %} to any template.
After I submit the form for the first time and then refresh the form it gets resubmitted and and I don't want that.
Here's my form in template :
<form action = "" method = "POST"> {% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Shout!"/>
</form>
How can I fix this ?
Here's my views:
def index(request):
shouts = Shout.objects.all()
if request.method == "POST":
form = GuestBookForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
shout = Shout(author = cd['author'], message = cd['message'])
shout.save()
form = GuestBookForm()
else:
form = GuestBookForm()
return render_to_response('guestbook/index.html', {'shouts' : shouts,
'form' : form },
context_instance = RequestContext(request))
My guess is that this is a problem in your view.
After successful submission and processing of a web form, you need to use a return HttpResponseRedirect, even if you are only redirecting to the same view. Otherwise, certain browsers (I'm pretty sure FireFox does this) will end up submitting the form twice.
Here's an example of how to handle this...
def some_view(request):
if request.method == "POST":
form = some_form(request.POST)
if form.is_valid():
# do processing
# save model, etc.
return HttpResponseRedirect("/some/url/")
return render_to_response("normal/template.html", {"form":form}, context_instance=RequestContext(request))
Given your recently added view above...
def index(request):
shouts = Shout.objects.all()
if request.method == "POST":
form = GuestBookForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
shout = Shout(author = cd['author'], message = cd['message'])
shout.save()
# Redirect to THIS view, assuming it lives in 'some app'
return HttpResponseRedirect(reverse("some_app.views.index"))
else:
form = GuestBookForm()
return render_to_response('guestbook/index.html', {'shouts' : shouts,
'form' : form },
context_instance = RequestContext(request))
That will use reverse to redirect to this same view (if thats what you are trying to do)
Try:
return redirect ('url', parameter_if_needed)
instead of
return render (request, 'name.hmtl', context)
In my case it works perfectly.
Most likely: When you refresh after submitting the form, you are showing the same form page again (without doing anything). You either need to redirect to the record page or a new page after the form has been submitted.
That way, the form becomes empty its data and will not resubmit when you refresh.
I have found a way and I think it's going to work for any website. what you have to do is add a Htmx cdn or you can call the javascript library from htmx.org like bootstrap CDN.
add this
before body tag
<script src="https://unpkg.com/htmx.org#1.6.0"></script>
add this or go to their website htmx.org
then what you have to do is go to your form tag and add this....
hx-post=" then add the path in here, where you want to redirect" something like this..
contact html
<form hx-post="/contact" hx-target="body" method="post">
</form>
you have to add a target depending on your form type. The above example is a contact form I want that contact form to stay on the same page and target its body like this hx-target="body"
views.py
return render(request, "blog/contact.html")
Use HttpResponseRedirect
create a new view(lets say thank_you) for successful message to display after form submission and return a template.
After successful form submission do return HttpResponseRedirect("/thank-you/") to the new thank-you view
from django.http import HttpResponseRedirect
def thank_you(request, template_name='thank-you.html'):
return render_to_response(template_name,locals(),context_instance=RequestContext(request))
and in urls.py
url(r'^thank-you/$','thank_you', name="thank_you")
Multiple form submission happens because when page refreshes that same url hits, which call that same view again and again and hence multiple entries saved in database. To prevent this, we are required to redirect the response to the new url/view, so that next time page refreshes it will hit that new url/view.
This solution worked for me. After form submission we have have to display a message in our template in form of popup or text in any form so though HttpResponseRedirect may prevent form resubmission but it won't deliver the message so here is what I did.
from django.contrib import messages
def index_function(request):
if request.method == "POST":
form = some_form(request.POST)
if form.is_valid():
# do processing
# save model, etc.
messages.success(request, 'Form successfully submitted') # Any message you wish
return HttpResponseRedirect("/url")
Then inside your template, you can show this message. Since this is global parameter you can display it in any HTML template like the following.
{% if messages %}
<div class="alert alert-success alert-dismissible">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
I am a newbie in Django. I am writing a sample application that has a form, submit that form then saving the data into the database. My form need to be validated before allowing to save data. So my question is how can I pass the error messages (that generated by validation) to the view? Any suggestion are welcome. Thanks!
Are you using a Form instance? Then you can render the form in the template and the error messages with automagically show up. For instance:
# views.py
def my_view(request, *args, **kwargs):
if request.method == 'POST':
form = MyForm(request.POST.copy())
if form.is_valid():
# Save to db etc.
elif request.methof == 'GET':
form = MyForm()
return render_to_response(..., {'form' : form})
And in the template:
{{ form.as_p }}
You will note that if the form is not vald (is_valid() returns False) then the view will proceed to return the form (with errors) to the template. The errors then get rendered in the template when form.as_p is called.
** Update **
As #Daniel said:
Even if you're not using form.as_p, you can get the errors for the whole form with form.errors and for each field with form.fieldname.errors.