Flask-WTForms: dynamically create name and id attributes - python

My form allows the user to enter a specific number of names for a specific number of basketball teams. These numbers are constantly updated so I need to create a dynamic form.
Let's say I have the following Flask view:
#app.route('/dynamic', methods=['GET', 'POST'])
def dynamic():
teams = ['Warriors', 'Cavs']
name_count = 2
return render_template('dynamic.html', teams=teams, name_count=name_count)
And the following form in the HTML template dynamic.html:
<form method='POST' action='/dynamic'>
{% for team_index in range(teams | count) %}
{% for name_index in range(name_count) %}
<input type="text"
class="form-control"
id="team{{ team_index }}_name{{ name_index }}"
name="team{{ team_index }}_name{{ name_index }}">
{% endfor %}
{% endfor %}
<form>
Which yields the following form:
<form method='POST' action='/dynamic'>
<input type="text" class="form-control" id="team0_name0" name="team0_name0">
<input type="text" class="form-control" id="team0_name1" name="team0_name1">
<input type="text" class="form-control" id="team1_name0" name="team1_name0">
<input type="text" class="form-control" id="team1_name1" name="team1_name1">
<form>
I love the Flask-WTF library so I'm wondering how I can use that (or simply wtforms) to render this form. I'm not sure this is even possible because wtforms requires a hardcoded field name for every input.

Figured this out. I need to use the WTForms Fieldlist and FormField enclosures.
class PlayerForm(FlaskForm):
player = Fieldlist(StringField('Player'))
class TeamForm(FlaskForm):
team = Fieldlist(FormField(PlayerForm))
#app.route('/dynamic', methods=['GET', 'POST'])
def dynamic():
teams = ['Warriors', 'Cavs']
name_count = 2
# Build dictionary to prepopulate form
prepop_data = {'team': [{'player': ['' for p in range(name_count)]} for team in teams]}
# Initialize form
form = TeamForm(data=prepop_data)
return render_template('dynamic.html', form=form)
And unpacking via jinja2 (id and name attributes on first field = team-0-player-0):
<form method="POST" action="/dynamic">
{{ form.csrf_token }}
{% for team in form.team %}
{{ team.csrf_token }}
{% for player in team.player %}
{{ render_field(player) }}
{% endfor %}
{% endfor %}
</form>

Related

How can I find the data which written with for loop in Flask

I'm trying to make a database project using with MYSQL and I'm new in Flask. In this project, the user should be able to increase and decrease the number of the products in the database. I print the items in my product table with for loop. The problem is I couldn't select the item which the user selected.
Html:
{% for mydata in data %}
<div class="items filterDiv {{ mydata[1] }}">
<img src="../static/foto/atayumurta.png" alt="">
<br><br>
<label for="item"> {{ mydata[3] }}</label>
<br><br>
<label class="Number">Number: <input class="numb" min="1" name="number" type="number"></label>
<br><br>
<input class="addbutton" type="submit" name="submit_button" value="Add">
<input class="removebutton" type="submit" name="removebutton" value="Remove">
</div>
{% endfor %}
Flask:
#app.route('/mainpage', methods=['POST','GET'])
def mainpage():
cursor.execute("SELECT * FROM product")
fetchdata = cursor.fetchall()
if request.method == 'POST':
if request.form['submit_button'] == 'Add':
number = request.form.get("number")
print(number)
return render_template('main.html', data=fetchdata)
else:
return render_template('main.html', data = fetchdata)
First, I wanted to see the number that the user has selected. So tried to print it but it prints null. As I said im new in flask, open for your suggestions.
Actually, the template contains a for loop for data.
In other words, the number field is not unique. So you need to modify the code. For example
{% for i in range(len(data)) %}
<div class="items filterDiv {{ data[i][1] }}">
<img src="../static/foto/atayumurta.png" alt="">
<br><br>
<label for="item"> {{ data[i][3] }}</label>
<br><br>
<label class="Number">Number: <input class="numb" min="1" name="number{{i}}" type="number"></label>
<br><br>
<input class="addbutton" type="submit" name="submit_button" value="Add">
<input class="removebutton" type="submit" name="removebutton" value="Remove">
</div>
{% endfor %}
Flask:
#app.route('/mainpage', methods=['POST','GET'])
def mainpage():
cursor.execute("SELECT * FROM product")
fetchdata = cursor.fetchall()
if request.method == 'POST':
if request.form['submit_button'] == 'Add':
number = request.form.get("number1")
print(number1)
return render_template('main.html', data=fetchdata)
else:
return render_template('main.html', data = fetchdata)

Multiple buttons in html, same view, different values

I'm trying to have my html display a session.counter on an HTML page. The counter should increment by a random number, with 4 different options for the range at which it increments (based on buttons). So far, I have all their actions routing to the same view, but I'm not sure how to code the values into the view! Can I nest multiple if-checks into the view? If so, do I need to redirect immediately after each check, or can it be after the if-checks?
EDIT: I've tried adding hidden inputs to make my view more efficient. I'm still a novice, so if the more descriptive your feedback, the easier it will be for me to implement changes and understand why!
Here's the html:
<div id="goldCount">
<h2>Gold Count: {{ session.request.goldCount }}</h2>
</div>
<div id="goldNode">
<h2>Blood Lotus Master</h2>
<h5>(earns 50 to 200 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="reap">
<button type="submit" name="reap">収める - Reap</button>
</form>
</div>
<div id="goldNode">
<h2>Shadow Dance Troupe</h2>
<h5>(earns 35 to 50 gold coins)</h5>
<form action='/process_gold' method = "post">
{% csrf_token %}
<input type="hidden" name="earn" value="weave">
<button type = "submit" name = "weave">織る - Weave</button>
</form>
</div>
<div id="goldNode">
<h2>Necromancy</h2>
<h5>(earns -200 to 200 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="summon">
<button type = "submit" name="summon">召喚 - Summon</button>
</form>
</div>
<div id="goldNode">
<h2>Clan War!</h2>
<h5>(earns -2000 to 2000 gold coins)</h5>
<form action='/process_gold' method="post">
{% csrf_token %}
<input type="hidden" name="earn" value="summon">
<button type="submit" name="war">影の戦争 - Shadow War</button>
</form>
</div>
and here is the views.py file
def index(request):
if 'goldCount' not in request.session:
request.session['goldCount'] = 0
return render(request, 'index.html')
def process_gold(request):
reap = random.randint(50,200)
weave = random.randint(35,50)
summon = random.randint(-200,200)
war = random.randint(-2000,2000)
print(request.POST)
if request.POST['earn'] == 'reap':
request.session['goldCount'] += random.randint(50,200)
if request.POST['earn'] == 'weave':
request.session['goldCount'] += random.randint(35,50)
if request.POST['earn'] == 'summon':
request.session['goldCount'] += random.randint(-200,200)
if request.POST['earn'] == 'war':
request.session['goldCount'] += random.randint(-2000,2000)
return redirect('/')
If you want to send goldCount through to HTML you need to use a render_template rather than a redirect. Store the goldCount into session then just call {{request.session.goldCount}} in your HTML.
Also, the correct if statement would be:
if 'goldCount' not in request.session:
request.session['goldCount'] = 0

Multiple forms with Flask keeps saying I haven't defined forms

I am trying to set up two different forms on the same Flask page with validators, but it keeps telling me my forms are not defined. I get error:
The code is designed to allow users to input a number and check if it is abundant, perfect, or deficient, and then with a separate form to allow users to define a range and get how many abundant, perfect or deficient numbers there are within that range.
My code is as follows:
from flask import Flask, render_template, request
from flask_wtf import Form
from wtforms import IntegerField
from wtforms.validators import InputRequired
from perfect_numbers import classify, listInRange
app = Flask(__name__)
app.config['SECRET_KEY'] = 'DontTellAnyone'
class PerfectForm(Form):
inputNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
class PerfectRangeForm(Form):
startNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
endNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
#app.route('/', methods=['GET', 'POST'])
def index():
form1 = PerfectForm(request.form, prefix="form1")
num = 1
Classify = classify(num)
if form.validate_on_submit() and form.data:
num = request.form1['inputNumber']
Classify = classify(form1.inputNumber.data)
return render_template('index.html', form1=form1, num=num, classify=Classify)
return render_template('index.html', num=1, form1=form1, classify=Classify)
#app.route('/aliRange', methods=['GET', 'POST'])
def aliRange():
form2 = PerfectRangeForm(request.form2, prefix="form2")
startNumber = 1
endNumber = 1
aliquot = 'abundant'
Classify = classify(num)
ListInRange = listInRange(startNumber, endNumber, aliquot)
if form2.validate_on_submit() and form2.data:
startNumber = request.form2['startNumber']
endNumber = request.form2['endNumber']
aliquot = request.form2['aliquot']
ListInRange = listInRange(startNumber, endNumber, aliquot)
return render_template('index.html', form2=form2, startNumber=startNumber, endNumber=endNumber, ListInRange=listInRange)
return render_template('index.html', form2=form2, startNumber=startNumber, endNumber=endNumber, ListInRange=listInRange)
if __name__ == '__main__':
app.run(debug=True)
index.html:
{% from "_formhelpers.html" import render_field %}
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>WTForms</title>
</head>
<body>
<div>
<form action="/" method="POST">
<dl>
{% if form1 %}
{{ form1.csrf_token }}
{{ render_field(form1.inputNumber) }}
{% endif %}
<input type="submit" value="submit1">
</dl>
</form>
</div>
<div>
{{ num }} is {{ classify }}
</div>
<div></div>
<div>
<form action="/aliRange" method="POST">
<div class="form-group">
<p>Input a start number and an end number to return a list of...</p>
<select class="form-control" action="/aliRange" name="aliquot" method="POST">
<option value = 'abundant'>Abundant</option>
<option value = 'perfect'>Perfect</option>
<option value = 'deficient'>Deficient</option>
</select>
<p>...numbers within that range</p>
<form action="/aliRange" method="POST">
<dl>
{% if form2 %}
{{ form2.csrf_token }}
{{ render_field(form2.startNumber) }}
{{ render_field(form2.endNumber) }}
{% endif %}
<input class="btn btn-primary" type="submit" value="submit">
</dl>
</form>
</div>
</form>
The {{ aliquot }} numbers between {{ startNumber }} and {{ endNumber }} are:
{{ listInRange }}
</div>
</body>
</html>
Error I get atm is: AttributeError: 'Request' object has no attribute 'form1'
EDIT:
You can simplify your code using a single view, using the submit value to differentiate the handling of the first form and the second one.
The modified code is:
class PerfectForm(Form):
inputNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
class PerfectRangeForm(Form):
startNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
endNumber = IntegerField('input a number', default=1, validators=[InputRequired(message='Please input an integer')])
aliquot = StringField('input a kind', default='perfect')
#app.route('/', methods=['GET', 'POST'])
def index():
form1 = PerfectForm(request.form, prefix="form1")
form2 = PerfectRangeForm(request.form, prefix="form2")
num = 1
Classify = classify(num)
startNumber = 1
endNumber = 1
aliquot = 'abundant'
ListInRange = listInRange(startNumber, endNumber, aliquot)
if request.form.get('submit') == 'submit-1':
if form1.validate_on_submit() and form1.data:
num = form1.data['inputNumber']
Classify = classify(num)
elif request.form.get('submit') == 'submit-2':
if form2.validate_on_submit() and form2.data:
startNumber = form2.data['startNumber']
endNumber = form2.data['endNumber']
aliquot = form2.data['aliquot']
ListInRange = listInRange(startNumber, endNumber, aliquot)
return render_template('index.html',
num=num, classify=Classify,
startNumber=startNumber, endNumber=endNumber, aliquot=aliquot, ListInRange=ListInRange,
form1=form1, form2=form2)
if __name__ == '__main__':
app.run(debug=True)
and the modified template index.html is:
{% from "_formhelpers.html" import render_field %}
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>WTForms</title>
</head>
<body>
<div>
<form action="/" method="POST">
<dl>
{{ form1.csrf_token }}
{{ render_field(form1.inputNumber) }}
<input type="submit" name="submit" value="submit-1">
</dl>
</form>
</div>
{% if num %}
<div>
{{ num }} is {{ classify }}
</div>
{% endif %}
<hr />
<div>
<form action="/" method="POST">
{{ form2.csrf_token }}
<div class="form-group">
<p>Input a start number and an end number to return a list of...</p>
<select class="form-control" action="/aliRange" name="aliquot" method="POST">
<option value = 'abundant'>Abundant</option>
<option value = 'perfect'>Perfect</option>
<option value = 'deficient'>Deficient</option>
</select>
<p>...numbers within that range</p>
<dl>
{{ render_field(form2.startNumber) }}
{{ render_field(form2.endNumber) }}
</dl>
<input class="btn btn-primary" type="submit" name="submit" value="submit-2">
</div>
</form>
<div>
The {{ aliquot }} numbers between {{ startNumber }} and {{ endNumber }} are:
{{ listInRange }}
</div>
</div>
</body>
</html>
OLD:
You are using form1 in the template but passing form inside the template context:
render_template('index.html', form=form1, num=num, classify=Classify)
You can either change form1 to form inside the template, or pass form1=form1 in the above line.
If you are rendering multiple forms inside the same template, you have to pass all the respective form variables: form1, form2, ... from all the views rendering that template. Otherwise the template rendering will raise the error you are seeing.
If you are interested in having a single form rendered among all the possible ones inside the template, you can use conditional rendering using
{% if form1 %}
<div>
<form action="/" method="POST">
<dl>
{{ form1.csrf_token }}
...
</dl>
</form>
</div>
{% endif %}
{% if form2 %}
<form action="/aliRange" method="POST">
...
</form>
{% endif %}
...
Also, your html seems incorrect to me, because you have a form nested inside another form. Not sure about what you are trying to obtain there.

Django - Taking value from POST request

I have a list of zones, identified by id (integer).
How can I get the zone that generated the post request?
manual.html
{% if zone_list %}
<ul>
{% for z in zone_list %}
<b><p>{{z.name}}</p></b>
<form action="" method="post">
{% csrf_token %}
<input type="submit" name="{{z.id}}" value="ON"/>
<input type="submit" name="{{z.id}}" value="OFF"/><br>
<br>
<label>Tiempo</label>:
<input type="integerfield" name="Tiempo">
<input type="submit" name="{{z.id}}" value="Start">
</form>
{% endfor %}
</ul>
{% endif %}
In the views.py I have to change the 1 for something that dynamically represents the zone
views.py
def manual(request):
if request.POST.has_key('1'):
z = Zone.objects.get(id = 1)
keyword = request.POST.get("1","")
if keyword == "ON":
#do something
if keyword == "OFF":
#do something
if keyword == "Start":
#do something
zone_list = Zone.objects.all()
context = {'zone_list':zone_list}
return render(request, 'irrigation_controller/manual.html', context)
I solved the problem. As themanatuf said, I used a hidden input field with the zone_id.
manual.html
{% if zone_list %}
{% for z in zone_list %}
<b><p>{{z.name}}</p></b>
<form action="" method="post">
{% csrf_token %}
<input type="hidden" name="zone_id" value="{{z.id}}">
<input type="submit" name="order" value="ON"/>
<input type="submit" name="order" value="OFF"/><br>
<br>
<label>Tiempo</label>:
<input type="integerfield" name="Tiempo">
<input type="submit" name="order" value="Start">
</form>
{% endfor %}
{% endif %}
And in the view I read the zone_id and the order.
views.py
def manual(request):
if request.POST.has_key('zone_id'):
z = Zone.objects.get(id = request.POST.get("zone_id",""))
keyword = request.POST.get("order","")
if keyword == "ON":
z.irrigation_on()
if keyword == "OFF":
z.irrigation_off()
if keyword == "Start":
tiempo = request.POST['Tiempo']
tiempo = float(tiempo)
irrigation_time.delay(z.id, tiempo)
zone_list = Zone.objects.all()
context = {'zone_list':zone_list}
return render(request, 'irrigation_controller/manual.html', context)

Storing variables with sessions django

This is my code:
views.py:
def some_function(request):
form = MyForm(request.POST)
if request.method == 'GET':
return render_to_response('template.html', RequestContext(request, {'form': form}))
elif request.method == 'POST':
input_word = request.POST['input_word']
if 'word_choice' in request.POST:
word_choice = request.POST['word_choice']
else:
word_choice = ''
var1 = HelperClass(input_word).func1()
table1 = HelperClass(input_word).table_one()
table2 = HelperClass(input_word).table_two()
word_selected = word_choice
content = {
'form': form,
'input_word': input_word,
'var1': var1,
'table1' : table1,
'table2' : table2,
'word_selected': word_selected,
}
return render_to_response('result_template.html', RequestContext(request, content))
else:
raise Http404
This is result_template.html:
{% block content %}
<form action="/admin/find-word/" method="post">
{% csrf_token %}
<input id="input_word" type="text" name="input_word" maxlength="100"><br />
<input type="submit" value="Submit" />
<form />
<h1>{% trans "You entered" %} "{{ input_word }}" </h1>
<p>{{ var1 }}</p>
<form action="/admin/find-root/" method="post">
{% csrf_token %}
<h3>{% trans "Table2" %}</h3>
{% for word in table2 %}
# Get info from database and display it on the current template.
<input type="radio" name='word_choice' value="{{ word }}"> {{ word }}<br>
{% endfor %}
<h3>{% trans "Table3" %}</h3>
{% for word in table3 %}
{# Get info from database and display it on the current template. #}
<input type="radio" name='word_choice' value="{{ word }}"> {{ word }}<br>
{% endfor %}
<p>{% trans "You selected: " %}{{ word_selected }}</p>
{# Submit the word of choice.#}
<input type="submit" value="Search">
<form />
{% endblock %}
I need to add code to views.py, so that:
it shows result, that was rendered after submitting the first form
(when assining input_word)
new result shows, when resubmitting the first form
when submitting second form redirect to success_template.html (this part I can do myself)
I know, that I need to use sessions here. I've tried different things, but I'm lost now.
Create a session variable in django as follows.
request.session['key'] = value
Access it by
request.session['key'] # or
request.session.get('key')
remove it by
del request.session['key']
In django you can simply assign values to sessions with:
request.session['value']
In your case you'd have to replace the word_selected variable with request.session['word_selected']

Categories

Resources