Please help - does flask/wtforms somehow treat the data property of a field differently after an append_entry call or am I just really doing this wrong?
I have a form that gets its data from a yaml file. On the initial GET request, the form populates showing the icons from the {% if ifc.poe.data %} as expected. If either button is hit to add a module or interface, the POST re-renders the page, but now ifc.poe.data is empty and thus no icons are rendered. If you comment out the if ifc.xxx.data portion and uncomment out the actual fields, the fields are rendered with the proper data every time. Is this something with how I'm building the form class or in how I'm handling the POST? What happened to the ifc.xxx.data?
Any help is appreciated, I'm pretty new at this.
forms.py
from flask_wtf import Form
from wtforms import Form as wtfForm # Bad hack to get around csrf in fieldlist
class DevInterface(wtfForm):
e_regex = '^ae(\d+)$'
ifc = StringField("Interface", validators=[DataRequired()])
poe = BooleanField('PoE', validators=[Optional()], default=False)
uplink = BooleanField('Uplink', validators=[Optional()],default=False)
desc = StringField("Description", validators=[Optional()],default='')
voip = StringField("VOIP", validators=[Optional()], default='')
etheropt = StringField("LAG interface", validators=[Optional(),Regexp(e_regex, message='Must designate an ae interface, eg. ae4')])
class DevHardware(wtfForm):
module = SelectField('Module', choices=[
('ex2200-24p','ex2200-24p'),('ex2200-48p','ex2200-48p'),
('ex4200-10g','ex4200-10g'),('ex4200-24f','ex4200-24f')],
default='ex2200-48p')
fpc = SelectField('FPC', choices=[(str(i),str(i)) for i in range(10)], default=0)
class DevOptions():
id = StringField('Device Serial Number', validators=[DataRequired()])
hostname = StringField('Hostname', validators=[DataRequired()])
make = SelectField('Make', choices=[('juniper','Juniper')], default = 'juniper')
class AddDev(Form, DevOptions):
modules = FieldList(FormField(DevHardware), min_entries=1)
interfaces = FieldList(FormField(DevInterface), min_entries=1)
add_ifc = SubmitField()
add_module = SubmitField()
views.py
#app.route('/editdev/<vspc>/<dev>', methods=['GET','POST'])
def editdev(vspc,dev):
from skynet.forms import AddDev
try:
d = s.loaddev(dev)
except IOError as e:
flash(dev + ' does not exist.', category='danger')
return redirect(url_for('editvspc', vspc=vspc))
# Have to change up how the data is presented for the form
d['id'] = dev
ifcs = d['interfaces']
del d['interfaces']
l = []
for i in ifcs:
j={}
j['ifc'] = i
j.update(ifcs[i])
l.append(j)
d['interfaces'] = sorted(l, key=lambda k: k['ifc'])
form = AddDev(request.form, data=d)
if form.add_ifc.data:
form.interfaces.append_entry()
elif form.add_module.data:
form.modules.append_entry()
elif request.method == 'POST' and form.validate():
# Placeholder for now
print 'Updated device'
for error in form.errors:
for e in form[error].errors:
flash(e, category='danger')
return render_template('adddev.html', form=form)
template
{% extends "layout.html" %}
{% import "bootstrap/utils.html" as util %}
{% block content %}
{{ super() }}
<div class="container-fluid">
<h1 align='center'>Add Device</h1>
<form method="post" action="">
{{ form.hidden_tag() }}
<div class="form-group">
<table class="table">
<tbody>
<tr>
<td>{{ form.id.label }}</td>
<td>{{ form.id(size=20) }}</td>
</tr>
<tr>
<td>{{ form.hostname.label }}</td>
<td>{{ form.hostname(size=20) }}</td>
</tr>
<tr>
<td>{{ form.make.label }}</td>
<td>{{ form.make}}</td>
</tr>
</tbody>
</table>
{{ form.add_module }}
<table class="table">
<tbody>
{% for field in form.modules.entries %}
<tr>
<td>{{ field.module.label }}</td>
<td>{{ field.module }}</td>
<td>{{ field.fpc.label }}</td>
<td>{{ field.fpc }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<table class="table">
<thead>
<tr>
<th>Interface</th>
<th>Description</th>
<th>PoE</th>
<th>VoIP</th>
<th>LAG</th>
<th>Uplink</th>
</tr>
</thead>
<tbody>
{% for ifc in form.interfaces %}
<tr>
<td>{{ ifc.ifc(size=10) }}</td>
<td>{{ ifc.desc }}</td>
<td>
{% if ifc.poe.data %}
{{ util.icon('flash', style='color:red') }}
{% endif %}
{% if ifc.voip.data %}
{{ util.icon('phone-alt', style='color:green') }}
{% endif %}
{% if ifc.etheropt.data %}
<a class="label label-success">{{ ifc.etheropt.data }}</a>
{% endif %}
{% if ifc.uplink.data %}
{{ util.icon('open', style='color:blue') }}
{% endif %}
</td>
{# <td>{{ ifc.poe }}</td>
<td>{{ ifc.voip }}</td>
<td>{{ ifc.etheropt }}</td>
<td>{{ ifc.uplink }}</td> #}
</tr>
{% endfor %}
</tbody>
</table>
{{ form.add_ifc }}
</div>
<button type="submit" class="btn btn-default">Add Device</button>
</form>
</div>
{% endblock %}
Related
I am trying to print some database values onto an HTML page.
The html code is run through a for loop that counts the amount of description values.
However it prints the entire database for each entry of Debit , Credit and Account Number.
I'm pretty sure the problem is inside the for loop structure , please assist.
Home.html:
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
{% extends "main/base.html"%}
{% block content%}
<h1> Kyle Database </h1>
<h2>Trial Balance</h2>
<br>
<br>
<table>
<th>Account</th>
<th>Description</th>
<th>Debit</th>
<th>Credit</th>
{% for xAlls in xAll %}
<tr>
<td>{{ accountNo }}</td>
<td>{{ description }}</td>
<td>{{ debit }}</td>
<td>{{ credit }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Views.py:
def home(request):
return render(request , 'main/home.html')
def Kyletrb(request):
desc = "SELECT Description FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor = cnxn.cursor();
cursor.execute(desc);
description = [tup[0] for tup in cursor.fetchall()]
accNo = "SELECT Account FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor.execute(accNo);
accountNo = [tup[0] for tup in cursor.fetchall()]
deb = "SELECT Debit FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor.execute(deb);
debit = [tup[0] for tup in cursor.fetchall()]
cred = "SELECT Credit FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor.execute(cred);
credit = [tup[0] for tup in cursor.fetchall()]
all = "SELECT Description, Account ,Credit,Debit FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor.execute(all);
xAll = [tup[0] for tup in cursor.fetchall()]
return render(request , 'main/Kyletrb.html' , {"description":description , "accountNo":accountNo , "debit":debit , "credit":credit , "xAll":xAll})
Output :
First of all, you can make a simple query for all data at once ( as you did in the last fetch ).
I would make a list of dicts like this:
def Kyletrb(request):
all = "SELECT Description, Account ,Credit,Debit FROM [Kyle].[dbo].[_btblCbStatement] WHERE Account <> ''"
cursor.execute(all);
xAll = cursor.fetchall()
cursor.close()
xAll_l = []
for row in xAll:
rdict = {}
rdict["Description"] = row[0]
rdict["Account"] = row[1]
rdict["Credit"] = row[2]
rdict["Debit"] = row[3]
xAll_l.append(rdict)
return render(request , 'main/Kyletrb.html' , {"xAlls":xAll_l})
After that you can make a for loop in template:
<table>
<th>Account</th>
<th>Description</th>
<th>Debit</th>
<th>Credit</th>
{% for xAll in xAlls %}
<tr>
<td>{{ xAll.Description }}</td>
<td>{{ xAll.Account }}</td>
<td>{{ xAll.Debit }}</td>
<td>{{ xAll.Credit }}</td>
</tr>
{% endfor %}
</table>
Try this code
{% for description in description %}
<tr>
<td>{{ description.accountNo }}</td>
<td>{{ description.description }}</td>
<td>{{ description.debit }}</td>
<td>{{ description.credit }}</td>
</tr>
{% endfor %}
You are using the same variable name in your for loop twice.
Normally it would be
for(description in descriptions)
Notice the plurality difference.
You can replace your for loop with the following code. and I hope it will work.
{% for desc in description %}
<tr>
<td>{{ desc.accountNo }}</td>
<td>{{ desc.description }}</td>
<td>{{ desc.debit }}</td>
<td>{{ desc.credit }}</td>
</tr>
{% endfor %}
and make sure that you have same objects of models given in your HTML template
Please try as below.
{% for description in descriptions %}
<tr>
<td>{{ description.accountNo }}</td>
<td>{{ description.description }}</td>
<td>{{ description.debit }}</td>
<td>{{ description.credit }}</td>
</tr>
{% endfor %}
I have figured out the formatting problem .
Instead of using a table, I have used 'div' tags for each column
.html :
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
{% extends "main/base.html"%}
{% block content%}
<h1>Kyle Database Trial Balance</h1>
<br>
<div class="container">
<div class="row mb-0">
<div class="col">
<h3>Account</h3>
{% for accountNo in accountNo %}
<p style="font-size:10px">{{ accountNo }}</p>
{% endfor %}
</div>
<div class="col-4">
<h3>Description</h3>
{% for description in description %}
<p style="font-size:10px">{{ description }}</p>
{% endfor %}
</div>
<div class="col">
<h3>Debit</h3>
{% for debit in debit %}
<p style="font-size:10px">{{ debit }}</p>
{% endfor %}
</div>
<div class="col">
<h3>Credit</h3>
{% for credit in credit %}
<p style="font-size:10px">{{ credit }}</p>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
Output :
I have context as below :
context = {
'author': author,
'books':books,
}
Now I need to use author and books in One for loop like this :
{% for each in ***author & books*** %}
<tr>
<td>{{ each.author.name }}</td>
<td>{{ each.book.name }}</td>
<td>{{ each.book.pubishDate }}</td>
</tr>
{% endfor %}
How to make such a for loop in Django template?
Thanks all.
join 2 dictionaries into a list before sending it to the template(i.e in the view):
dicts = [dict1, dict2]
and try this:
{% for d in dicts %}
<tr>
<td> {{ d.x }} </td>
<td> {{ d.y }} </td>
</tr>
{% endfor %}
Using FlaskForm and SelectMultipleField, I'm creating a table of choices allowing for multiple selection to be ranked: 1, 2, 3, ... .
In order to place the choices in a grid pattern on a table, I made each row it's own instance of SelectMultipleField.
The submit button is only returning the values of the first instance of SelectMultipleField (dogs).
How can I get the submit button to return values in all instances of SelectMultipleField?
Here's the class in my forms module:
class LPRForm(FlaskForm):
party = ['Dogs', 'Cats', 'Rabbits', 'Birds']
dog = [('', 'D. Duke'), ('', 'R. Rusty'), \
('', 'T. Tucker'), ('', 'R. Roger')]
cat = [('', 'S. Shadow'), ('', 'M. Misty'), \
('', 'P. Patch'), ('', 'P. Paws')]
rabbit = [('', ''), ('', 'C. Clover'), ('', ''), ('', '')]
bird = [('', 'P. Pikachu'), ('', 'S. Starburst'), \
('', ''), ('', 'F. Flighty')]
vote_dog = SelectMultipleField('District', choices=dog,
option_widget=widgets.TextInput() )
vote_cat = SelectMultipleField('District', choices=cat,
option_widget=widgets.TextInput() )
vote_rabbit = SelectMultipleField('District', choices=rabbit,
option_widget=widgets.TextInput() )
vote_bird = SelectMultipleField('District', choices=bird,
option_widget=widgets.TextInput() )
submit = SubmitField('Cast Ballot')
Here's the relevant protion of the html file:
<table style="width:100%" align="center">
<tr>
<td> </td>
{% for vote in form.vote_dog %}
{% if vote.label != '': %}
<td>{{ vote(size="2") }}</td> <td>{{ vote.label }}</td>
{% endif %}
{% endfor %}
</tr>
<tr>
<td> </td>
{% for vote in form.vote_cat %}
{% if vote.label != '': %}
<td>{{ vote(size="2") }}</td> <td>{{ vote.label }}</td>
{% endif %}
{% endfor %}
</tr>
<tr>
<td> </td>
{% for vote in form.vote_rabbit %}
{% if vote.label != '': %}
<td>{{ vote(size="2") }}</td> <td>{{ vote.label }}</td>
{% endif %}
{% endfor %}
</tr>
<tr>
<td> </td>
{% for vote in form.vote_bird %}
{% if vote.label != '': %}
<td>{{ vote(size="2") }}</td> <td>{{ vote.label }}</td>
{% endif %}
{% endfor %}
</tr>
</table>
</td>
</tr>
{{ form.submit }}
And the view module:
#app.route('/lpr', methods=['GET', 'POST'])
def lpr():
form = LPRForm()
return render_template('lpr.html', title='Home', form=form)
I was able to get this working by using FormField. However, the documentation gives a default separator as -. Additionally, I explicitly identified separator='-', but only when I used . as a separator, when calling the class, did it work properly, ... even with separator='-'.
I'm using Django to make a website for gym trainers.
what I want is a template for the daily schedules of all trainers like this
But, my page is
The problem is the 'td' of per trainer's tr repeats as many as the number of schedules the trainer has. I know the {% for sc in schedules %} is the problem. But, because schedules are the query set, I should use the for and while using for, I should check the right time to insert the schedule to the right tr, td position. How can I make the successful table to show the daily schedules of all users(trainers)?? Anybody will be very helpful to me.
Schedule.model
class Schedule(models.Model):
Trainer = models.ForeignKey(settings.AUTH_USER_MODEL,blank=True,null=True, related_name='schedule', on_delete=models.SET_NULL)
title = models.CharField(max_length=12,blank=True,)
start = models.DateTimeField(null=True, blank=True)
my views.py
def staff_daily_schedule_search(request):
all_schedules = Schedule.objects.all()
Fitness_list = User.objects.filter(groups__name='Fitness') # Fitness Trainers
search_date1 = request.GET.get('search_date','')
search_date= datetime.datetime.strptime(search_date1, '%Y-%m-%d') #%T = %H:%M:%S '%Y-%m-%d'
schedules= Schedule.objects.none()
for f in Fitness_list:
sc = f.schedule.filter(start__year=search_date.year).filter(start__month = search_date.month).filter(start__day = search_date.day)
print(sc)
schedules |= sc
context = {
'search_date' : search_date1 if search_date1 else datetime.date.today(),
'Fitness_list':Fitness_list,
'schedules' : schedules,
}
return render(request, 'management/staff_daily_schedule.html', context)
staff_daily_schedule.html
<form action="{% url 'management:staff_daily_schedule_search' %}" method="GET">
<span><input type="date" class="search_date my-control" name="search_date" value="{{ search_date }}" ></span>
<a id="today" class="btn btn-warning">오늘</a>
<button class="btn btn-info" value="검색" >검색</button>
</form>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>06:00 </th>
<th>07:00 ~ 07:50</th>
<th>08:00 ~ 08:50</th>
<th>09:00 ~ 09:50</th>
<th>10:00 ~ 10:50</th>
</tr>
</thead>
<tbody>
{% for trainer in Fitness_list %}
<tr>
<td>{{ trainer }} </td>
{% for sc in schedules %} <!-- because of this for, td repeats as many as the number of schedule per trainer has..-->
{% if sc.Trainer == trainer %}
{% if sc.start.hour == 21 %} <!--HOUR of 6:00 a.m = 21-->
<td>{{ sc }}</td>
{% else %}
<td ></td>
{% endif %}
{% if sc.start.hour == 22 %}
<td>{{ sc }}</td>
{% else %}
<td ></td>
{% endif %}
{% if sc.start.hour == 23 %}
<td>{{ sc }}</td>
{% else %}
<td ></td>
{% endif %}
{% if sc.start.hour == 0 %} <!-- 9 a.m. -->
<td>{{ sc }}</td>
{% else %}
<td></td>
{% endif %}
{% if sc.start.hour == 1 %}
<td>{{ sc }}</td>
{% else %}
<td></td>
{% endif %}
{% endif %}
{% endfor %}
</tr>
{% endfor %} <!-- tr repetition as trainers number-->
</tbody>
</table>
The problem
If you put the logic in your view instead of the template, it's easy to set up the table exactly like you want it.
[Edited to use an OrderedDict to preserve the order of trainers.]
views.py
def staff_daily_schedule_search(request):
Fitness_list = User.objects.filter(groups__name='Fitness') # Fitness Trainers
search_date1 = request.GET.get('search_date','')
search_date= datetime.datetime.strptime(search_date1, '%Y-%m-%d') #%T = %H:%M:%S '%Y-%m-%d'
trainer_dict = OrderedDict()
# Initialize each row of the table with the trainer's name and a blank schedule
for f in Fitness_list:
trainer_dict[str(f.id)] = {'name': f.get_full_name(), 'hour_21': '',
'hour_22': '', 'hour_23': '', 'hour_0': '', 'hour_1': ''}
schedules = Schedule.objects.filter(start__year=search_date.year).filter(start__month =
search_date.month).filter(start__day = search_date.day)
# Insert each schedule item into the appropriate table row and cell
for sc in schedules:
trainer_dict[str(sc.Trainer.id)]['hour_' + str(sc.start.hour)] = sc.title
context = {
'search_date' : search_date1 if search_date1 else datetime.date.today(),
'trainer_dict': trainer_dict
}
return render(request, 'management/staff_daily_schedule.html', context)
staff_daily_schedule.html
<form action="{% url 'management:staff_daily_schedule_search' %}" method="GET">
<span><input type="date" class="search_date my-control" name="search_date" value="{{ search_date }}" ></span>
<a id="today" class="btn btn-warning">오늘</a>
<button class="btn btn-info" value="검색" >검색</button>
</form>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>06:00 </th>
<th>07:00 ~ 07:50</th>
<th>08:00 ~ 08:50</th>
<th>09:00 ~ 09:50</th>
<th>10:00 ~ 10:50</th>
</tr>
</thead>
<tbody>
{% for trainer in trainer_dict %}
<tr>
<td>{{ trainer.name }}</td>
<td>{{ trainer.hour_21 }}</td>
<td>{{ trainer.hour_22 }}</td>
<td>{{ trainer.hour_23 }}</td>
<td>{{ trainer.hour_0 }}</td>
<td>{{ trainer.hour_1 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
This question already has answers here:
Using index from iterated list
(2 answers)
Closed 7 years ago.
I'm passing 4 lists of the same length and the length of the lists to my template. How do I iterate over the lists?
views.py
def allbooks(request):
ID = [1,2]
bookName = ["Python", "Java"]
author = ["idk", "who"]
copies = [3,7]
return render(request, 'allbooks.html',{'ID':ID,'bookName':bookName, 'author':author, 'copies':copies,'range':range(len(ID))})
allbooks.html
{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
<div>
<h3>
List of books:
</h3>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Book Name</th>
<th>Author</th>
<th>Number of copies</th>
</tr>
</thead>
<tbody>
{% for x in range %}
<tr>
<td>{{id[x]}}</td>
<td>{{bookName[x]}}</td>
<td>{{author[x]}}</td>
<td>{{copies[x]}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<h3>You must login to continue.</h3>
{% endif %}
{% endblock %}
I've tried replacing the variable x with {% forloop.counter0 %} but to no avail. How can I fix this?
Zip all your lists to one list of tuples:
books= zip(ID, bookName, author, copies)
return render(request, 'allbooks.html',{ "books": books} )
Than loop over it in templates like:
{% for book in books %}
<tr>
<td>{{ book.0 }}</td>
<td>{{ book.1 }}</td>
<td>{{ book.2 }} </td>
<td>{{ book.3 }}</td>
</tr>
{% endfor %}
I suggest a better structure for your book info:
books = [
{ "id":1, "name": "Python", "author":"idk", "copies": 1},
{ "id":2, "name": "Java", "author":"idk2", "copies": 3}
]
than iterate through it:
{% for book in books %}
<tr>
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.author }} </td>
<td>{{ book.copies }}</td>
</tr>
{% endfor %}