How do I dynamically generate forms with JINJA - python

What is the best way to generate a full styled form using flask and jinja
<div class="form">
{{ form.statement_reference.label() }}
{{ form.statement_reference(value=username) }}
</div>
<div class="form">
{{ form.date.label() }}
{{ form.date() }}
</div>
<div class="form">
{{ form.full_name.label() }}
{{ form.full_name() }}
</div>
<div class="form">
{{ form.address_line_1.label() }}
{{ form.address_line_1() }}
</div>
<div class="form">
{{ form.city.label() }}
{{ form.city() }}
</div>
If I pass them to the template in a list and then iterate over the list, how to I create the label() as well?
something like this;
{% for item in my_list %}
{{ form.{item}.label() }}
{% endfor %}
How would i create these but without having to explicitly stating each one?

Related

Flask WTForms {{ form.input(value=something.something) }}

I'm trying to render an html form and pass in a table row with sqlalchemy. So then the form will show up with all it's input fields filled with the row data, with the purpose of editing and sending it back to the db. But somehow the form is displaying only two input fields, both of type text. Integer fields and radio fields are not showing the row's each corresponding value, although the values exist and are not none.
Here's the route:
#app.route('/editar/<string:id>')
def hotel_edit(id):
form = HotelForm()
viaje = arribos.query.get(id)
return render_template('edit_hotel.html', form=form, viaje=viaje)
Here's the html file displaying the wtform object + trying to get the object "viaje" to show the values with no luck:
{% block body %}
<div id="main_form">
<form action="/" method="post" id="ingresos">
{{ form.hidden_tag() }}
<fieldset>
<div class="checks" id="inout">
{{ form.check.label }}
{{ form.check(value=viaje.check) }}
</div>
<div class="horafecha">
{{ form.fecha.label }}
{{ form.fecha(value=viaje.fecha) }}
{{ form.hora.label }}
{{ form.hora(value=viaje.hora) }}
</div>
<div class="textbox">
{{ form.vuelo.label(class="inputtext") }}
{{ form.vuelo(class="inputtext", value=viaje.vuelo) }}
{{ form.habitacion.label(class="inputtext") }}
{{ form.habitacion(class="inputtext", value=viaje.habitacion) }}
{{ form.huespedes.label(class="inputtext") }}
{{ form.huespedes(class="inputtext", value=viaje.huespedes) }}
{{ form.valijas.label(class="inputtext") }}
{{ form.valijas(class="inputtext", value=viaje.valijas) }}
</div>
<div class="checks" id="puerto">
{{ form.puerto.label(class="puertos") }}
{{ form.puerto(class="puertos", value=viaje.puerto) }}
</div>
<div class="buttons">
{{ form.enviar }}
</div>
</fieldset>
</form>
</div>
{% endblock %}
And here's the form (not styled yet):
form
As you can see, most of the input fields are missing the value. Does anyone know how to fix this?
Also: I can show you the form.py file that has the class definition for the entire form, if that helps.
You can populate the form before it's sent to the jinja template from within your route. It would look something like this:
#app.route('/editar/<string:id>')
def hotel_edit(id):
form = HotelForm()
viaje = arribos.query.get(id)
form.check.data = viaje.check
form.fecha.data = viaje.fecha
form.hora.data = viaje.hora
...
return render_template('edit_hotel.html', form=form)
You can then just refer to the form field without passing the value arg.
{% block body %}
<div id="main_form">
<form action="/" method="post" id="ingresos">
{{ form.hidden_tag() }}
<fieldset>
<div class="checks" id="inout">
{{ form.check.label }}
{{ form.check() }}
</div>
<div class="horafecha">
{{ form.fecha.label }}
{{ form.fecha() }}
{{ form.hora.label }}
{{ form.hora() }}
</div>
<div class="textbox">
{{ form.vuelo.label(class="inputtext") }}
{{ form.vuelo(class="inputtext") }}
{{ form.habitacion.label(class="inputtext") }}
{{ form.habitacion(class="inputtext") }}
{{ form.huespedes.label(class="inputtext") }}
{{ form.huespedes(class="inputtext") }}
{{ form.valijas.label(class="inputtext") }}
{{ form.valijas(class="inputtext") }}
</div>
<div class="checks" id="puerto">
{{ form.puerto.label(class="puertos") }}
{{ form.puerto(class="puertos") }}
</div>
<div class="buttons">
{{ form.enviar }}
</div>
</fieldset>
</form>
</div>
{% endblock %}

For some FlaskForm validators, error message does not pop up

While "DataRequired" and "NumberRange" produce a pop-up error message, "EqualTo" (and my own custom "MoreThan") do not. How can I get them to also produce a pop-up message?
####################################################################
My form has three fields. "min_nFeatures" has to be lower than "max_nFeatures", so I changed the code of the "EqualTo" validator to "MoreThan" (as advised in Code a validator for a WTForms form which compares the inputs from two fields). The validator is working: if the user enters a larger value for "min_nFeatures", it does not go through (it returns to the same page). However, there is no pop-up message as with the other built-in validators (for example, if the user does not enter anything, there's a pop-up "Please fill out this field"). I would like the same behavior for the custom validator.
My code:
class MoreThan(object):
def __init__(self, fieldname, message=None):
self.fieldname = fieldname
self.message = message
def __call__(self, form, field):
try:
other = form[self.fieldname]
except KeyError:
raise ValidationError(field.gettext("Invalid field name '%s'.") % self.fieldname)
if field.data <= other.data:
d = {
'other_label': hasattr(other, 'label') and other.label.text or self.fieldname,
'other_name': self.fieldname
}
message = self.message
if message is None:
message = field.gettext(
'The maximal number of expressed genes has to be larger than the minimal number of expressed genes')
raise ValidationError(message)
class vln_plot_form(FlaskForm):
min_nFeatures = IntegerField('* Minimal number of expressed genes:', validators=[DataRequired()])
max_nFeatures = IntegerField('* Maximal number of expressed genes:',
validators=[DataRequired(), MoreThan('min_nFeatures')])
max_mtpercent = IntegerField('Maximal percent of mitochondrial gene expression:', validators=[NumberRange(1, 100)])
submit = SubmitField('Submit')
view:
#app.route('/vln', methods=['POST', 'GET'])
def violin_plots():
...
form = vln_plot_form()
if request.method == 'POST':
if form.validate_on_submit():
...
return redirect(url_for('next_page'))
return render_template('violin_plots.html', form=form)
I read Message not flashing on some WTForm validation methods but could not apply it to my case.
########################################################################
edit:
Here's my HTML code:
{% extends 'base.html' %}
{% block head %}
<title>HELLO</title>
{% endblock %}
{% block body %}
<center><h1>Run!</h1></center>
<h2>Step 2/3</h2>
<figure>
<img src={{ vln_plot_file }} align="middle" alt="vln_plot" style="width:70%">
</figure>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-horizontal">
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.min_nFeatures.label }}</label>
{{ form.min_nFeatures }}
</div>
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.max_nFeatures.label }}</label>
{{ form.max_nFeatures }}
</div>
{% if with_mt %}
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.max_mtpercent.label }}</label>
{{ form.max_mtpercent }}
</div>
{% endif %}
<div class="form-group">
{{ form.csrf_token() }}
{{ form.submit(class="btn btn-primary")}}
</div>
</div>
{% if form.errors %}
{{ form.errors }}
{% endif %}
</form>
{% endblock %}
Since I added "{{ form.errors }}" at the end of the HTML, I do see the correct form.errors at the bottom of the webpage, but this is of course a very ugly way to display the errors...
This should do the trick:
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-horizontal">
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.min_nFeatures.label }}</label>
{{ form.min_nFeatures }}
{% for error in form.min_nFeatures.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.max_nFeatures.label }}</label>
{{ form.max_nFeatures }}
{% for error in form.max_nFeatures.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
{% if with_mt %}
<div class="form-group col-md-6">
{{ form.csrf_token() }}
<label for=""> {{ form.max_mtpercent.label }}</label>
{{ form.max_mtpercent }}
{% for error in form.max_mtpercent.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</div>
{% endif %}
<div class="form-group">
{{ form.csrf_token() }}
{{ form.submit(class="btn btn-primary")}}
</div>
</div>
</form
Note how I manage the display of errors for each of the fields individually.

How to show both of my buttons on same line?

I'll post both entire form elements even though it might not all be needed to answer the question.
Both buttons are at very end of this code snippet.
I haven't been able to successfully add any row divs, etc. since a form is ending in the middle of the div, and another beginning, it doesn't seem to work.
Any advice?
Thank you.
<div class="row content-section col-md-10">
<form method="POST" action="">
{{ form.csrf_token() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">
{% if sales_order_number %}
<h3>Sales Order Number: {{ sales_order_number }}</h3>
{% else %}
<h3>?? No Sales Order Number ??</h3>
{% endif %}
{% if total_controllers_configured %}
<h3>Total Controllers Configured for this job: {{ total_controllers_configured }} / {{ sales_order_controller_quantity }} </h3>
{% else %}
<h3> Total Controllers Configured for this job: 0 / {{ sales_order_controller_quantity }} </h3>
{% endif %}
<br>
Configure Controller {{ current_controller }} Below:
</legend>
<!-- Fan Quantity and Modbus Ranges -->
<div class="row">
<div class="form-group col-md-8">
{{ form.modbus_ranges.label(class="form-control-label") }}
{% if form.modbus_ranges.errors %}
{{ form.modbus_ranges(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.modbus_ranges.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.modbus_ranges(class="form-control form-control-lg", id="modbus-ranges") }}
{% endif %}
</div>
<div class="form-group col-md-4">
{{ form.fan_quantity.label(class="form-control-label") }}
{% if form.fan_quantity.errors %}
{{ form.fan_quantity(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.fan_quantity.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.fan_quantity(class="form-control form-control-lg" , id="fan-quantity") }}
{% endif %}
</div>
</div>
<!-- fan type, controller type, serial number -->
<div class="row">
<div class="form-group col-md-6">
{{ form.controller_type.label(class="form-control-label") }}
{% if form.controller_type.errors %}
{{ form.controller_type(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.controller_type.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.controller_type(class="form-control form-control-lg") }}
{% endif %}
</div>
<div class="form-group col-md-6">
{{ form.controller_serial_number.label(class="form-control-label") }}
{% if form.controller_serial_number.errors %}
{{ form.controller_serial_number(class="form-control form-control-lg is-invalid") }}
<div class="invalid-feedback">
{% for error in form.controller_serial_number.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.controller_serial_number(class="form-control form-control-lg") }}
{% endif %}
</div>
</div>
<div class="form-check">
{{ form.gfa_fire_NC(class="form-check-input") }}
{{ form.gfa_fire_NC.label(class="form-check-label") }}
</div>
<div class="form-group text-left col-md-6">
{{ form.save_configuration(class="btn btn-outline-info") }}
</div>
</fieldset>
</form>
<form action="" method="POST" novalidate>
{{ form.csrf_token() }}
<div class="form-group text-right col-md-6">
<input type='submit' class="btn btn-outline-danger" name='clear_configuration' value="Clear Configuration"></input>
<!-- {{ form.clear_configuration(class="btn btn-outline-danger", required=False) }} -->
</div>
</form>

Form Values are not getting added to the database in Flask

I posted this question earlier but it was then linked to a similar question which don't provide the required solution and was then closed for answering.
So I have created a Flask Application that tracks the movement of products from one location to another while I make movements via the Flask App the form doesn't get validated I tried adding {{ form.hidden_tag() }} and {{ form.csrf_token }} to the html file that takes input from the user.
If I run this application from the terminal on my command line the form is validated and gets added to the database but if I run the flask app and submit the form in the browser it doesn't.
here is my code for the same
class MovementForm(FlaskForm):
to_location = SelectField('To Location', coerce=int)
from_location = SelectField('From Location', coerce=int)
product = SelectField('Product')
quantity = IntegerField('Quantity')
add_movement = SubmitField('Add Movement')
#app.route('/movements',methods=["GET","POST"])
def add_movements():
form = MovementForm()
form.to_location.choices = [(location.id, location.location_name) for location in Location.query.all()]
form.from_location.choices = [(location.id, location.location_name) for location in Location.query.all()]
form.product.choices = [(product.id, product.product_name) for product in Product.query.all()]
form.from_location.choices.insert(0, (0, 'None'))
if form.validate_on_submit():
new_movement = Movement(to_location_id=form.to_location.data, from_location_id=form.from_location.data, product_id=form.product.data, quantity=form.quantity.data)
db.session.add(new_movement)
db.session.commit()
flash('Product has been moved!', 'success')
return redirect(url_for('add_movements'))
return render_template('add_movements.html', form=form)
Here is my html file
<form action="/movements" method="post">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
<div class="row">
<div class="form-group col">
{{ form.from_location.label(class="form-control-label") }}
{{ form.from_location(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.to_location.label(class="form-control-label") }}
{{ form.to_location(class="form-control form-control-lg") }}
</div>
</div>
<div class="row">
<div class="form-group col">
{{ form.product.label(class="form-control-label") }}
{{ form.product(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.quantity.label(class="form-control-label") }}
{{ form.quantity(class="form-control form-control-lg") }}
</div>
</div>
<div class="form-group">
{{ form.add_movement(class="btn btn-outline-info") }}
</div>
</form>
What's wrong here?
Try to remove to change in the HTML form the form's action.
<form action="" method="post">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
<div class="row">
<div class="form-group col">
{{ form.from_location.label(class="form-control-label") }}
{{ form.from_location(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.to_location.label(class="form-control-label") }}
{{ form.to_location(class="form-control form-control-lg") }}
</div>
</div>
<div class="row">
<div class="form-group col">
{{ form.product.label(class="form-control-label") }}
{{ form.product(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.quantity.label(class="form-control-label") }}
{{ form.quantity(class="form-control form-control-lg") }}
</div>
</div>
<div class="form-group">
{{ form.add_movement(class="btn btn-outline-info") }}
</div>
</form>
Does this solve the issue?
Also What I suggest you is to add the Flash message into the HTML, because I see that once the Form is submitted it returns back to the 'add_movements' function. Therefore add this:
<div>
{% for msg in get_flashed_messages%}
<h1>{{msg}}</h1>
{% endfor %}
</div>
<form action="" method="post">
{{ form.hidden_tag() }}
{{ form.csrf_token }}
<div class="row">
<div class="form-group col">
{{ form.from_location.label(class="form-control-label") }}
{{ form.from_location(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.to_location.label(class="form-control-label") }}
{{ form.to_location(class="form-control form-control-lg") }}
</div>
</div>
<div class="row">
<div class="form-group col">
{{ form.product.label(class="form-control-label") }}
{{ form.product(class="form-control form-control-lg") }}
</div>
<div class="form-group col">
{{ form.quantity.label(class="form-control-label") }}
{{ form.quantity(class="form-control form-control-lg") }}
</div>
</div>
<div class="form-group">
{{ form.add_movement(class="btn btn-outline-info") }}
</div>
#EDIT
I noticed that something is missing in the product field once coerce in missing:
class MovementForm(FlaskForm):
to_location = SelectField('To Location', coerce=int)
from_location = SelectField('From Location', coerce=int)
product = SelectField('Product', coerce=int)
quantity = IntegerField('Quantity')
add_movement = SubmitField('Add Movement')
EDIT #2
In case you run in this kind of issues (which happens all the time) I suggest you to add a print statements and a If/Else clause as well. This will dramatically help you where the issue is ( The issue on your type of problem you posted is that you 'don't see it') and will give you 'eyes.'
For example this is what I would have done:
#app.route('/movements',methods=["GET","POST"])
def add_movements():
form = MovementForm()
form.to_location.choices = [(location.id, location.location_name) for
location in Location.query.all()]
form.from_location.choices = [(location.id, location.location_name)
for location in Location.query.all()]
form.product.choices = [(product.id, product.product_name) for product
in Product.query.all()]
form.from_location.choices.insert(0, (0, 'None'))
if form.validate_on_submit():
print('Form Ok') #if you see the 'Form ok' to see if is validated
new_movement = Movement(to_location_id=form.to_location.data,
from_location_id=form.from_location.data,
product_id=form.product.data, quantity=form.quantity.data)
db.session.add(new_movement)
db.session.commit()
flash('Product has been moved!', 'success')
return redirect(url_for('add_movements'))
else:
print('Form Not Ok') #If you see this printed then you see that
#is not validated
return render_template('add_movements.html', form=form)

Safe built-in inside for loop Django CKEditor

I'm using CKEditor with Django and when i need show some RTF code in my template, usually, I use the safe built in filter (autoscape).
Example:
<p class="card-text">{{ questao.enunciado|safe }}</p>
But, how to use safe built-in inside a for loop?
{% for field in form %}
<div class="fieldWrapper">
<strong>{{ field.label_tag }}</strong>
{{ field|safe }}
{% if field.help_text %}
<p class="help">{{ field.help_text }}</p>
{% endif %}
</div>
{% endfor %}
This way above is not working for me, and in the template it ends up showing me HTML codes in text format.
You need not use the safe for the field.
Try:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
Also refer django documentation
You are applying safe filter to the wrong value. You have to apply it to field.label or field.help_text instead, e.g.:
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
<label for="{{ field.id_for_label }}">{{ field.label|safe }}</label>
{{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}

Categories

Resources