I am trying to generate a dynamic list of student from database, using FieldList and FormField. The fields are being generated using the data from database, but if I try to change something from the front-end, the back-end receives only the starting data (e.g., for checkbox it only gets False)
form:
class Beneficiary_Migration_Entry_Form(FlaskForm):
name = StringField('')
id = StringField('')
school = SelectField('', validate_choice=False)
student_class = SelectField('', validate_choice=False)
migrate = BooleanField('')
class Beneficiary_Migration_Form(FlaskForm):
entry_form_list = FieldList(FormField(Beneficiary_Migration_Entry_Form))
submit = SubmitField('Submit')
route
#app.route('/migration/student/<school_year_id>', methods=['GET', 'POST'])
#login_required
def student_migration_page(school_year_id):
school_year_id = int(school_year_id)
school_year = School_Year.query.filter(School_Year.id==school_year_id).first()
entry_form_list = []
school_list = Database_Manager.get_school_list(current_user)
school_choices = get_school_list()
entries = Student.query.filter(Student.user_id==current_user.id).all()
for entry in entries:
form = Beneficiary_Migration_Entry_Form(
name=entry.name,
id=entry.id,
)
id_str = str(entry.id)
form.name.name = 'name-' + id_str
form.school.name = 'school-' + id_str
form.migrate.name = 'migrate-' + id_str
form.student_class.name = 'student_class-' + id_str
form.id.name = 'id-' + id_str
form.school.choices = school_choices
form.student_class.choices = [('','')]
entry_form_list.append(form)
migration_form = Beneficiary_Migration_Form()
migration_form.entry_form_list = entry_form_list
school_map = Database_Manager.get_schools_map(current_user)
if migration_form.submit.data:
for form in migration_form.entry_form_list:
print(form.migrate.data)
return redirect(url_for('migration_page', school_year_id=school_year_id))
return render_template('migration-elev.html', school_map=school_map,school_year=school_year, migration_form=migration_form)
Front-end:
<form action="" method="post">
{{ migration_form.hidden_tag() }}
<table class="table table-striped table-dark" style="text-align: center;">
<thead class="thead-dark">
<tr>
<th scope="col">ID</th>
<th scope="col">Nume</th>
<th scope="col">Scoala</th>
<th scope="col">Clasa/ Grupa</th>
<th scope="col">Transfer</th>
</tr>
</thead>
<tbody>
{% for form in migration_form.entry_form_list %}
{{ form.hidden_tag() }}
<tr>
<td>{{form.id.value}}</td>
<td>{{form.name}}</td>
<td>{{form.school}}</td>
<td>{{form.student_class}}</td>
<td>{{form.migrate}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>{{ migration_form.submit(class_="btn btn-primary") }}</p>
</form>
At first it only got the data from the first entry from the FieldList, but I've found that I need to put unique names to work.
I've tried to use validate_on_submit but I get AttributeError: 'Beneficiary_Migration_Form' object has no attribute 'copy'.
I've also tried to use dictionaries and named tuples to load data, but I get the same result.
I've modified name to entry_name, since name is a reserved word.
To load data I've used append_data, where I've made a dictionary with data from db.
Related
I’m new to Django and Python and have been plugging through Python Crash Course. I have finished that and now have been making my own Django project, adding some extra bits in.
Currently, I have a table of entries from my model which I am displaying with a template, each entry in the table has a time and some other data. What I would like is the value for the cumulative time in a column, next to each entries time ie
Lesson
Lesson time
cumulative time
Lesson1
0.5
0.5
Lesson2
1
1.5
Lesson3
1.3
2.8
I’m just really stuck on how to get this done, I have looked at lot’s of stack overflow and tried various ways that I just can’t get to work correctly. Looking at other examples they use annotate() and cumsum, but I’ve not been able to get that to work. I’ve done count and sum of other values but this has got me stuck. I think I need to loop over it in my view, but not sure how to associate each value to a cumulative time.
Any help would be greatly received.
Also, sorry this isn't very well written or succinct, programming doesn't come naturally to me.
My topics.py
def topics(request):
"""Show all the topics"""
# Get the public topics
public_topics = Lessontopic.objects.filter(public=True).order_by('date_added')
# Get the private topics
if request.user.is_authenticated:
private_topics = Lessontopic.objects.filter(owner=request.user).order_by('date_added')
topics = private_topics
"""By Adding private into topics, seeing all public topics not just users
this way any topic added by authenticated user comes up in private, even if
it is also visible in public"""
time = Lessontopic.objects.aggregate(Sum('lesson_time'))
total_time = time.get('lesson_time__sum')
total_time = float(total_time)
lessons = Lessontopic.objects.all().count()
print(f"There has been {lessons} lessons ")
solo = (Lessontopic.objects.filter(solo_lesson=True)
.aggregate(Sum('lesson_time')))
solo_time = solo.get('lesson_time__sum')
print(f"solo lesson flying time {solo_time} hours")
solo_num = Lessontopic.objects.filter(solo_lesson=True).all().count()
dual = (Lessontopic.objects.filter(solo_lesson=False)
.aggregate(Sum('lesson_time')))
dual_time = dual.get('lesson_time__sum')
remaining_time = 50 - total_time
#Make a pie chart.
labels = ['Solo', 'Dual']
values = [solo_time, dual_time]
chat = ['this is long']
# Visualise the results
data=[Pie(labels=labels,text=chat,values=values,pull=[0.1, 0],
textposition='outside')
]
x_axis_config = {'title': 'Hours'}
y_axis_config = {'title': 'Lesson type'}
my_layout = Layout(title='Flying time Solo vs Dual Instruction',title_font_color='black' ,
title_font_size=30, title_x=0.5, legend_bordercolor='green', legend_borderwidth=5 )
fig = ({'data': data, 'layout': my_layout})
div = offline.plot(fig, auto_open=False, output_type='div')
cumulative_time = Lessontopic.objects.annotate(
cumulative_times=Window(
expression = Sum('lesson_time'),
order_by=F('date_added').asc(),
frame=RowRange(start=None, end=0)
)
)
context = {'topics': topics, 'cumulative_time':cumulative_time,'lessons':lessons, 'solo_time':solo_time,
'solo_num':solo_num, 'dual_time':dual_time,'solo_num':solo_num,
'remaining_time':remaining_time, 'dual':dual,'graph':div,}
else:
topics = public_topics
context = {'topics': topics}
return render(request, 'flying_logs/topics.html', context)
my model.py
from django.db import models
from django.contrib.auth.models import User
from django.forms.widgets import DateInput
from django import forms
import json
import requests
# Create your models here.
class Lessontopic(models.Model):
"""The topic of each lesson or day flying"""
text= models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
public = models.BooleanField(default=False, verbose_name="Make post public?",
help_text="click on this form if you want it to be viewed by anyone")
lesson_number = models.PositiveIntegerField(blank=True, null=True)
lesson_time = models.DecimalField('enter flight time', max_digits=2, decimal_places=1)
solo_lesson = models.BooleanField(default=False, verbose_name="Were you solo for the lesson",
help_text="Click if were flying solo for the lesson")
runway_direction = models.PositiveIntegerField(
verbose_name="Select runway direction: 07 or 25")
number_landings = models.PositiveIntegerField(blank=True, default=1,
help_text="Enter how many landings")
date = models.DateTimeField()
time_of_lesson = models.PositiveIntegerField(
verbose_name="time that the lesson started")
weather_data = models.CharField(max_length=200)
adding = models.TextField(default=None, null=True, blank=True)
def __str__(self):
"""Return a string representation of the modeel"""
return self.text
My template topics.html
{% extends "flying_logs/base.html" %}
<h1>Topics</h1>
{% block page_header %}
{% endblock page_header %}
{% block content %}
<div class="container">
<table class="table text-center table-hover table-bordered" >
<tr><tr class="table-primary">
<th style="width: 10%">Lesson No.</th>
<th scope="col">Date</th>
<th scope="col">Lesson</th>
<th scope="col">Dual/Solo
<th scope="col">Flying time</th>
<th scope="col">Cumulative</th>
</tr>
</thead>
{% for topic in topics %}
{% if topic.solo_lesson %}
<tr class="table-success">
{% endif %}
<td class="text-center"> {{topic.lesson_number}}</td>
<td>{{ topic.date|date:'d/m/Y'}}</td>
<td>{{ topic }}</td>
<td>{{ topic.solo_lesson|yesno:"Solo, Dual" }}</td>
<td class="text-center">{{topic.lesson_time}}</td>
<td>Time so far{{ cumulative_time}}</td>
</tr>
{% endfor %}</table>
</div>
<div class="container">
<table class="table text-center table-hover table-bordered" >
<thread>
<tr><tr class="table-primary">
<th style="width: 5%">Solo</th>
<th style="width: 20%">Dual</th>
<th style="width: 20%">total time</th>
<th style="width: 20%">Time remaing</th>
<th style="width: 20%"> Solo Lessons</th>
<th style="width: 35%">Number of lessons</th>
</tr>
</thead>
<td>{{solo_time|floatformat:1}}</td>
<td>{{dual_time|floatformat:1}}</td>
<td>{{total_time}}</td>
{% if remaining_time >= 0 %}
<td class="table-danger">{{ remaining_time|floatformat:1 }}</td>
{% elif remaining_time <= 0 %}
<td class="table-success">You've done the minimum hours!!</td>
{% endif %}
<td class="text-center"> {{ solo_num}} </td>
<td>{{lessons}}</td>
</tr>
</tr>
</table>
</div>
<h3><a href="{% url 'flying_logs:new_topic' %}"><button name="new entry"
class="btn btn-primary">Add a new topic</button></a></h3>
{% if graph %}
<div style="width:600;height:500">
{{ graph|safe }}
</div>
{% endif %}
{% endblock content %}
I want to search title with database record. Database record may contain sub-string in many cell which i want to search. I want to render all the records but could not shows because "TypeError: 'Store' object is not iterable" occurs.
My application.py is:
def bookresult():
isbn = request.form.get("isbn")
title = request.form.get("title")
author = request.form.get("author")
year = request.form.get("year")
submit = []
session = Session()
result = session.query(Store).all()
for result in result:
if title in result.title:
return render_template("bookresult.html",results=result)
return render_template("login.html", message="Not found")
session.commit()
And my bookresult.html is:
{% block body %}
<h1> Search result book </h1>
<table>
<tr>
<th> ISBN number </th>
<th> Book title </th>
<th> Author name </th>
<th> Publish year </th>
</tr>
<tr>
{% for result in results %}
<td> {{ result.isbn }} </td>
<td> {{ result.title }} </td>
<td> {{ result.author }} </td>
<td> {{ result.year }} </td>
{% endfor %}
</tr>
</table>
{% endblock %}
Above code works if for loop in html file is removed. But how to get all the records without making iteration in html file.
How to get records iteratively in html file if my search string matched with database cell ?
Issue occurs in
a) if .. is .. True syntax
b) rendering in templates doesn't iterate over and over. You have to pass at once.
c) {% for i in range(count) %} will cause iteration for int object i.e we can access elements of list as list[i] only.
Now application.py will look like:
#app.route("/bookresult", methods=['POST'])
def bookresult():
isbn = request.form.get("isbn")
title = request.form.get("title")
author = request.form.get("author")
year = request.form.get("year")
session = Session()
list1 = []
list2 = []
list3 = []
list4 = []
results = session.query(Store).all()
# return render_template("message.html",result=type(results))
for i in results:
if (title in i.title) is True:
list1.append(i.isbn)
list2.append(i.title)
list3.append(i.author)
list4.append(i.year)
# counter = counter + 1
# else:
# counter = counter + 1
# if (title in i) is True:
# return render_template("bookresult.html",results=i)
# counter = counter + 1
# else:
# counter = counter + 1
# return render_template("bookresult.html", results=list1, list2, list3, list4)
return render_template("bookresult.html", count=len(list1), result1=list1, result2=list2, result3= list3, result4= list4)
session.commit()
And bookresult.html will be:
{% for i in range(count) %}
<tr>
<td> {{ result1[i] }} </td>
<td> {{ result2[i] }} </td>
<td> {{ result3[i] }} </td>
<td> {{ result4[i] }} </td>
</tr>
{% endfor %}
How can I get the Field Names to return as text/string?
Highlighted "Field Names" I want returned as text, not fields
I am dynamically creating a list of fields and then appending values. But I can't seem to figure out a way to return the field names as plain text. The below code appends them to a field (fieldname)-- which is the only way I have been able to return them.
class ContractFields(FlaskForm):
fieldname = StringField()
fieldvalue = StringField()
class ContractForm(FlaskForm):
title = StringField('title')
contractfieldlist = FieldList(FormField(ContractFields))
#app.route('/tester.html', methods=['GET','POST'])
def contractfields():
form = ContractForm()
for f in object:
document_form = ContractFields()
document_form.fieldname = f.name #need this list object to return as table text, not a field
document_form.fieldvalue = f.value
form.contractfieldlist.append_entry(document_form)
return render_template('tester.html', form = form)
And from the template:
<div>
<form action="" method="post" name="form">
{{ form.hidden_tag() }}
<div>
<table>
<tr>
<th> ListNumber </th>
<th> Field Name </th>
<th> Field Value </th>
</tr>
{% for items in form.contractfieldlist %}
<tr>
<td>{{ items.label }}</td>
<td>{{ items.fieldname }}</td>
<td>{{ items.fieldvalue }}</td>
</tr>
{% endfor %}
</table>
</div>
<p><input type="submit" name="edit" value="Send"></p>
</form>
</div>
My experience with Python has largely been limited ETL and data transformation so I don't understand why this was so complicated. But after way too many hours I finally found the following solution worked for me.
Specifically modifying the associated excerpt from the above post to be the following:
class ContractFields(FlaskForm):
fieldname = HiddenField()
fieldvalue = StringField()
def __init__(self, *args, **kwargs):
super(ContractFields, self).__init__(*args, **kwargs)
if 'obj' in kwargs and kwargs['obj'] is not None:
self.fieldvalue.label.text = kwargs['obj'].fieldname
And the template html to:
<td>{{ items.label }}</td>
<td>{{ items.fieldvalue.label }}</td>
<td>{{ items.fieldvalue }}</td>
I have a problem: I want to make checkboxes of each line of table:
<form action="" method="post">
{% csrf_token %}
<table>
<thead>
<tr>
<th>cb</th>
<th width="150">first_col</th>
<th>sec_col</th>
<th width="150">third_col</th>
</tr>
</thead>
<tbody>
{% for i in list %}
<tr>
<td><input type="checkbox" name="choices" value="{{i.id}}"></td>
<td>{{ i.created_date}}</td>
<td> {{ host }}/{{i}}/ </td>
<td>{{i.number_of_clicks}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit" name="delete" class="button">Del</button>
</form>
And in the def I make next in order to check if it works:
if 'delete' in request.POST:
for item in request.POST.getlist('choices'):
print (item)
But it does not print anything... What do i do wrong? Or can you help me to write correct handler of checkboxes?
First you should check for request.method == 'POST' rather than for the submit button name in request.POST. Though, that shouldn't be the problem why you don't see anything. From what you posted I don't know what's not working but here's an example that shows how you could achive what you want. It assumes your template is in test.html:
# This is just a dummy definition for the type of items you have
# in your list in you use in the template
import collections
Foo = collections.namedtuple('Foo', ['id', 'created_date', 'number_of_clicks'])
def test(request):
# check if form data is posted
if request.method == 'POST':
# simply return a string that shows the IDs of selected items
return http.HttpResponse('<br />'.join(request.POST.getlist('choices')))
else:
items = [Foo(1,1,1),
Foo(2,2,2),
Foo(3,3,3)]
t = loader.get_template('test.html')
c = RequestContext(request, {
'list': items,
'host': 'me.com',
})
return http.HttpResponse(t.render(c))
I'm struggling to see how this is done, and the documentation doesn't seem to help much.
I need to generate a table, the row size will be variable, but not dynamic (i know how much rows i need before generating the page).
For the sake of simplicity lets imagine a page where you grade n exams with an integer.
i tried this:
the form.
class InputInteger(Form):
grade = IntegerField('Grade')
the view
#decorator..
def grade():
form = InputInteger()
names = student_list
return render_template("grade.html", form=form, names=names)
the template
<table>
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
{% for name in names %}
<tr>
<td>
{{name}}
</td>
<td>
{{form.grade}}
</td>
</tr>
</table>
But how do i read back the inputed values?
How do i distinguish who's grade that belongs too?
Am fairly confused, i've read about FieldList(FormField(IntegerField)), but isn't that just one field with a list of integers?
What about the Table Widget, do i need that?
Please help.
For anyone looking at this now, the OP was correct to think about FieldLists and FormFields. Here is a solution:
forms.py:
class GradeForm(FlaskForm):
student = IntegerField('Student ID')
grade = IntegerField('Grade')
delete = BooleanField('Delete')
class GradeFormSet(FlaskForm):
gradeset = FieldList(FormField(GradeForm), min_entries=0)
view.py:
def grade():
# create a dict of student IDs and their initial grades (or None for now)
init_merits = [dict(student=s.id, grade=None) for s in myStudentTable.query.all()]
gradeform = GradeFormSet(gradeset=init_merits)
if form.validate_on_submit():
...
# meritforms.data['gradeset'] contains a list of dictionary values for further processing
# check 'delete' == True to handle deletion of that student from your table
...
return render_template('template.html', form=gradeform)
Template:
<table>
{% for merit in form.gradeset %}
<tr>
<td>{{ merit.placing(readonly=true) }} {{ merit.csrf_token }} {{ merit.hidden_tag() }}</td>
<td>{{ merit.grade }}</td>
<td>{{ merit.delete }}</td>
</tr>
{% endfor %}
</table>
<input type="submit" name="finish" value="Save">
<input type="submit" name="cancel" value="Cancel">
You're almost right. Put your table inside a html form and catch in a function where you can retrieve your input fields.
Here is an example:
<form action="/grade">
<table>
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
{% for name in names %}
<tr>
<td>{{name}}</td>
<td><input id='{{name}}' value='{{ form.grade }}'></td>
</tr>
</table>
</form>
And your Flask function:
#app.route('/grade', methods=['GET', 'POST'])
def grade():
if request.method == 'POST':
return 'Form posted.'
When you post your form to your function, you can access your input field by this way: request.form['inputfieldname'] and then you do your stuff. I hope my explanation is clear.