I've used an identical form to submit and it has worked in the past. The submit button isn't working now, so everything within the POST and validate loop is not being executed.
This is a single page webapp. This form should take 7 fields and write to the db. The app does properly print the records (recent_data) from the db, so the connection to the db is correct.
Expected behavior: This should produce a POST-GET redirect. The submit button should trigger a POST, and it should redirect to the homepage, with the form now empty.
Current Behavior: The submit button does not do anything.
Reproduce: I think this could be reproduced with the plain boilerplate flask app code added to my code here.
Debugging: There is no error displayed.
class ApplicationForm(FlaskForm):
emp_length_cat_input = IntegerField(
u'How long have you been with your employer? (1-11)',
validators=[DataRequired()])
home_status_input = IntegerField(
u'What is your housing status? (1-Rent, 2-Other, 3-Mortgage, 4-Own)',
validators=[DataRequired()])
zip3_input = IntegerField(
u'What is the first 3 digits of your zip code?',
validators=[DataRequired()])
total_acc_input = IntegerField(
u'How many accounts have you ever had in your name?',
validators=[DataRequired()])
annual_inc_input = IntegerField(
u'What is your annual income? (no commas)',
validators=[DataRequired()])
dti_input = IntegerField(
u'What is your debt-to-income ratio? (round to 2 decimals)',
validators=[DataRequired()])
descr_input = StringField(
u'Why do you need this loan? (enter text)',
validators=[DataRequired()])
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
# Set form
form = ApplicationForm(request.form)
# Form submission
if request.method == 'POST' and form.validate_on_submit():
session['emp_length_cat_input'] = form.emp_length_cat_input.data
session['home_status_input'] = form.home_status_input.data
session['zip3_input'] = form.zip3_input.data
session['total_acc_input'] = form.total_acc_input.data
session['annual_inc_input'] = form.annual_inc_input.data
session['dti_input'] = form.dti_input.data
session['descr_input'] = form.descr_input.data
with db.connect() as conn:
conn.execute(
"""INSERT INTO
loans_tbl (
emp_length_cat,
home_status,
zip3,
total_acc
annual_inc,
dti,
descr)
VALUES (%s,%s,%s,%s,%s,%s,%s)""",
(emp_length_cat_input, home_status_input, zip3_input,
total_acc_input, annual_inc_input, dti_input, descr_input)
)
db.session.commit()
flash('Thanks for applying')
return redirect(url_for('index'))
return render_template('index.html',
form=form,
emp_length_cat_input=session.get('emp_length_cat_input'),
home_status_input=session.get('home_status_input'),
zip3_input=session.get('zip3_input'),
total_acc_input=session.get('total_acc_input'),
annual_inc_input=session.get('annual_inc_input'),
dti_input=session.get('dti_input'),
descr_input=session.get('descr_input')
)
<form method="POST" action="" novalidate>
{{ form.hidden_tag() }}
{{ form.emp_length_cat_input.label }} {{ form.emp_length_cat_input(id='emp_length_cat') }} </br>
{{ form.home_status_input.label }} {{ form.home_status_input(id='home_status') }} </br>
{{ form.zip3_input.label }} {{ form.zip3_input(id='zip3') }} </br>
{{ form.total_acc_input.label }} {{ form.total_acc_input(id='total_acc') }} </br>
{{ form.annual_inc_input.label }} {{ form.annual_inc_input(id='annual_inc') }} </br>
{{ form.dti_input.label }} {{ form.dti_input(id='dti') }} </br>
{{ form.descr_input.label }} {{ form.descr_input(id='descr') }} </br>
{{ form.submit() }}
</form>
If you're going to use form as a POST method you can simply put a button within your form inside your template to submit to form, and that should send a POST request which will be your form submitted.
<form method="post">
...
<button type="submit">Submit</button>
</form>
You must specify the type of the button. I solved the problem like this
class ApplicationForm(FlaskForm):
...
submit = SubmitField(
'Submit',
render_kw={'type':'submit'})
And you need to fix the action path
<form method="POST" action="/">
Update:
I believe something was wrong with my submit button. I used {{ wtf.quick_form(form) }} to render the form and it worked! Thank you.
Related
I've created a form which by submit uploads an item to the database. The problem is that if I press f5 it'll submit the form again, because of the URL is now different.
I have these two url patterns
urlpatterns = [
url(r'(?i)^CMS/$', views.CMS, name='CMS'),
url(r'^createItem/$', views.createItem, name='createItem')
]
and my view looks like this
def CMS(request):
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
def createItem(request):
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
pass
form = itemCreateForm()
context = {
'form' : form,
'message' : 'ItemCreated!'
}
return render(request, 'CMS.html', context)
the CMS.html
{% if message %}
{{ message }}
{% endif %}
<div class='newItemFields'>
<form action="{% url 'kar:createItem' %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
my form
class itemCreateForm(ModelForm):
class Meta:
model = item
fields = ['name', 'type', 'price']
I start at homepage/CMS/ and fill in the form and press submit, and view function createItem runs and creates and saves the object in the database. And sends the user to homepage/CMS/createItem. And now everytime the user press f5 the createItem function will run again and insert another object into the database with the same values as the previous one, even though the input fields are empty (can't wrap my head around that).
I also twice write form = itemCreateForm() which I believe is dubious?
What I'd like to do is after createItem is run, it should send the user back to homepage/CMS/ and not homepage/CMS/createItem. Would that be the proper way to do it? Or is there a smart way of doing this.
At the end of your createItem function, you are rendering HTML of the page rather than redirecting. Instead, you need to do
return HttpResponseRedirect(reverse('kar:index'))
You will need to import HttpResponseRedirect and reverse which is used to resolve the URL through its name.
Check this out: https://docs.djangoproject.com/en/1.10/topics/forms/#the-view
What I'd like to do is after createItem is run, it should send the
user back to homepage/CMS/ and not homepage/CMS/createItem. Would that
be the proper way to do it? Or is there a smart way of doing this.
That would indeed be the proper and smart way to do it. Have one view handle both GET and POST and then redirect after successful form submission. This ensures that the user can't resubmit the form merely by refreshing. And you address your concern about repeating your code.
urlpatterns = [
url(r'(?i)^$', views.index, name='index'),
url(r'^createItem/$', views.createItem, name='createItem')
]
Then combine your views
def createItem(request):
if request.method == 'POST':
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
return HttpResponseRedirect('/homepage/CMS/')
else :
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
Note that the code is now shorter, it gives proper feedback to the user when the form is not valid. And you can't refresh to submit the for twice. We need a small change to the template
<div class='newItemFields'>
<form action=method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
The message display part isn't needed anymore
I added a newsletter sign-up form to the footer area of my site and such had to use an inclusion_tag because I couldn't bind it to a view. It works well and as expected, but I have a strange thing happening that I apparently am not smart enough to figure out myself :)
After the form is submitted, I receive the email confirmation, but two things happen:
The Django Success Message doesn't appear until after I manually refresh the page.
Where my form sits, there are syntax 'Missing Variable' errors. I included a screenshot for reference and my form code is below. The form fields re-appear and errors go away after refreshing the page again.
home_tags.py
#register.inclusion_tag('pages/tags/footer_newsletter_signup.html', takes_context=True)
def footer_newsletter_signup(context):
request = context['request']
title = 'Newsletter Signup'
form = MailingListForm(request.POST or None)
if form.is_valid():
mailing_list_full_name = form.cleaned_data.get('mailing_list_full_name')
mailing_list_phone = form.cleaned_data.get('mailing_list_phone')
mailing_list_email = form.cleaned_data.get('mailing_list_email')
mailing_list_subject = 'Submission from Newsletter Signup'
mailing_list_message = 'Yes, please add me to marketing emails.'
from_email = settings.DEFAULT_FROM_EMAIL
recipient_list = [from_email, 'charles#studiorooster.com']
ctx = {
'mailing_list_subject': mailing_list_subject,
'mailing_list_full_name': mailing_list_full_name,
'mailing_list_email': mailing_list_email,
'mailing_list_phone': mailing_list_phone,
'mailing_list_message': mailing_list_message
}
message = get_template('pages/newsletter_signup_email.html').render(Context(ctx))
msg = EmailMessage(mailing_list_subject, message, to=recipient_list, from_email=from_email)
msg.content_subtype = 'html'
msg.send()
messages.success(request, "Thank you, you've been added to our list.")
return HttpResponse('/')
context = {
'form': form,
'title': title,
}
return context
footer_newsletter_signup.html
<form action='' method='POST' role='form' class="form-inline">
{% csrf_token %}
<div class="form-group">
{{ form.mailing_list_full_name }}
</div>
<div class="form-group">
{{ form.mailing_list_phone }}
</div>
<div class="form-group">
{{ form.mailing_list_email }}
</div>
<button class="button button-lg button-square button-pasific hover-ripple-out" type='submit'>Subscribe</button>
</form>
Then I just add the tag to my template like:
{% footer_newsletter_signup %}
Answering this
Ok, so here is where I am confused. I have a dozen views and this form is a Call-to-Action form that sits at the top of the footer. How do I bind this form to every view without repeating the code everywhere? Thank you for your help.
You need to create separate view to handle this form and provide action param in form tag pointing to this view.
Here is general idea, code my not work
#template
<form action='{% url "send-mail" %}' method='POST' role='form' class="form-inline">
...
#views
def send_mail(request):
form = MailingListForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
mailing_list_full_name = form.cleaned_data.get('mailing_list_full_name')
mailing_list_phone = form.cleaned_data.get('mailing_list_phone')
mailing_list_email = form.cleaned_data.get('mailing_list_email')
mailing_list_subject = 'Submission from Newsletter Signup'
mailing_list_message = 'Yes, please add me to marketing emails.'
from_email = settings.DEFAULT_FROM_EMAIL
recipient_list = [from_email, 'charles#studiorooster.com']
ctx = {
'mailing_list_subject': mailing_list_subject,
'mailing_list_full_name': mailing_list_full_name,
'mailing_list_email': mailing_list_email,
'mailing_list_phone': mailing_list_phone,
'mailing_list_message': mailing_list_message
}
message = get_template('pages/newsletter_signup_email.html').render(Context(ctx))
msg = EmailMessage(mailing_list_subject, message, to=recipient_list, from_email=from_email)
msg.content_subtype = 'html'
msg.send()
messages.success(request, "Thank you, you've been added to our list.")
return HttpResponse('/')
#tags
#register.inclusion_tag('pages/tags/footer_newsletter_signup.html', takes_context=True)
def footer_newsletter_signup(context):
title = 'Newsletter Signup'
form = MailingListForm()
context = {
'form': form,
'title': title,
}
return context
#url
url('r^send-mail/$', send_mail, name='send-email')
Here's the goal: when the user submits the form, use one view to send the submitted data to the database, then redirect back to the form, but with the data pre-populated. This is mostly working, but something about my implementation is wrapping extra quotes around the string. For now, I'm just using a super-simple form, btw. I enter Billy, and the pre-pop is: "Billy", if I click submit again, it comes back as: "\"Billy\"", then "\"\\\"Billy\\\"\"", and so on (as far as I have tested, anyways.
relevant views are:
def editUsers(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = usersForm(request.POST)
# check whether it's valid:
# process the data in form.cleaned_data as required
# redirect to a new URL:
name = json.dumps(form.data['user_name'])
request.session['editUserName'] = name
# call out to limboLogic.py to update values
test = name
return redirect('../users')
# if a GET (or any other method) we'll create a blank form
else:
return redirect('../users')
from .forms import *
def users(request):
form = None
if 'editUserName' not in request.session:
# create a blank form
form = usersForm()
else:
# form = equipmentForm(initial='jim') - used to make sure I was branching the if/else correctly
form = usersForm(initial={'user_name':request.session['editUserName']}, auto_id=False) #limboLogic.GetUserInfo(name))
return render(request, 'limboHtml/UserManagement.html', {'form': form})
form is simply:
class usersForm(forms.Form):
user_name = forms.CharField(label='New User\'s name', max_length=100)
and the template is:
{% extends "base.html" %}
{% block content %}
<div class="row">
<p>This is the user management page</p><br>
<form action="/edit/users.html" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="OK">
</form>
<br><p class="bold">This is below the form</p>
</div>
{% endblock %}
thoughts?
I can't quite say what the intracies are here, but the problem involves the fact that I was using a json class. I used this site as a guide and managed to fix the problem. note that the key aspect is inside the second if:
name = form.cleaned_data['user_name'] works fine,
name = json.dumps(form.data['user_name']) does not
the whole function as it now stands:
def editUsers(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = usersForm(request.POST)
# check whether it's valid:
# process the data in form.cleaned_data as required
# redirect to a new URL:
if form.is_valid():
name = form.cleaned_data['user_name']
# name = json.dumps(form.data['user_name'])
request.session['editUserName'] = name
# call out to limboLogic.py to update values
test = name
return redirect('../users')
# if a GET (or any other method) we'll create a blank form
return redirect('../users')
New to Django and having problem seeing form fields displayed. What I see is just the submit button. If pressed, the form is finally presented, but with the format for a form that had bad data (typical 'this field is required' error for each box, red box, etc).
The form works fine after entering data and again pressing submit (stores entries in my db). I have a number of forms on the same page that have the same behavior.
Example of one form:
#model
class dbPara(models.Model): #parameters
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
username = models.CharField(max_length=10)
turns = models.FloatField(default=27)
units = models.FloatField(default=5)
rise = models.FloatField(default=2.9)
rescutL = models.FloatField(default=0.0833333333)
rescutH = models.FloatField(default=0.333333333)
LorR = models.CharField(max_length=1, default='R')
def __str__(self):
return self.timestamp, self.username, self.turns, self.units, self.rise, self.rescutL, self.rescutH, self.LorR
#form
class ParaForm(ModelForm):
class Meta:
model = dbPara
widgets = {'username': forms.HiddenInput()}
fields =['username', 'turns', 'units', 'rise', 'rescutL', 'rescutH', 'LorR']
#view
def importParameters(request):
if request.method == 'GET':
form = ParaForm()
else:
form = ParaForm(request.POST)
if form.is_valid():
entry=dbPara(username = request.POST.get('username'),
turns = request.POST.get('turns'),
units = request.POST.get('units'),
rise = request.POST.get('rise'),
rescutL = request.POST.get('rescutL'),
rescutH = request.POST.get('rescutH'),
LorR = request.POST.get('LorR')
)
entry.save()
return render(request, 'main.html',
{'ParaHTML' : form })
#url
urlpatterns = patterns('Inputs.views',
url(r'^importParameters/$', 'importParameters', name='urlParameters'),
)
#main.html
<div class='col-lg-3'>
<h4>Set Rosetta Parameters</h4>
<action="{% url "urlParameters" %}" method="post">{% csrf_token %}
{{ ParaHTML|crispy }}
<input type="hidden" name = "username" value = "{{ user.get_username }}">
<input type="submit" class="btn btn-primary" value="Set">
</form>
</div>
Appreciate any advice (better simple than 'most correct but complicated')
Could it be due to using default in the model? Would that not 'fill in the form' and result in 'POST' at the initial visit to the page, resulting in just the button? Thoughts?
One Suggesestion here ....
if Using request.POST.get('anything') simply then it Will raise error if particular string not find as in example('anything') string...
Because request.POST.get('anything') will return None if 'anything' is not in request.POST.
Additionally, .get allows you to provide an additional parameter of a default value which is returned if the key is not in the dictionary.
e.g: Corrected will be request.POST.get('anything', 'mydefaultvalue')
I am new to django forms and Crispy Forms. I have some simple forms in a little forum Im developing. I think I don't need to use the %crispy% tag. I only need the form|crispy filter. However, I don't know why they don't render the error messages.
Also, if I want to customize the error messages (they must be in spanish), do I need to use the %crispy% tag or is it possible to do this with the |crispy filter?
Anyway, here is one of my forms:
from django import forms
from django.forms import Textarea
class FormNuevoVideo(forms.Form):
url = forms.URLField(initial='http://', max_length=250)
titulo = forms.CharField(max_length=150)
descripcion = forms.CharField(
help_text="...",
widget=Textarea(attrs={'rows': 3, 'data-maxlength': 500}))
Here is the view:
#login_required
def nuevo_video(request, slug):
template = 'videos/nuevo.html'
tema = Temas.objects.get(slug=slug)
if request.method == 'POST':
form = FormNuevoVideo(request.POST)
if form.is_valid():
...
nuevo_video.save()
return redirect('videos:videos_tema', slug=tema.slug, queryset='recientes')
else:
return redirect('videos:nuevo_video', slug=tema.slug) #this same view.
else:
form_nuevo_video = FormNuevoVideo()
context = {'form_nuevo_video': form_nuevo_video, 'tema': tema}
return render(request, template, context)
And in the HTML:
{% block form %}
<form action = "{% url 'videos:nuevo_video' tema.slug %}" method = "post">
{% csrf_token %}
{{form_nuevo_video|crispy}}
<input class = "btn pull-right" type = "submit" value ="enviar"/>
</form>
{% endblock form %}
So, lets say, when someone tries to submit a video with a title of more than 150 characters, it doesn't display the error. I am sure I am missing something simple. Also, I'd like to customize the error messages so that they are in spanish. Thanks in advance.