Flask - Jinja 2 template - rendering a bootstrap list select box - python

My web app created by Flask is using Jinja 2 template. I created my form from the request form value as below:
form = T3InputForm(request.form)
And defined my T3InputForm as:
class T3InputForm(Form):
savgp = StringField('Selected Averaging Period',
validators=[DataRequired()])
In my HTML file, I have
<div class="form-group col-md-6">
<p> <h4>{{ form.savgp.label}} : {{ form.savgp }} </h4></p>
</div>
So I can assign my input to the {{ form.savgp }} variable and pass it to my python function. Now, My question is that, How can I render a bootstrap list select box and assign the value of that to the form.savgp variable using Jinja 2 template:
To render a list select box using bootstrap I know this is the code lines:
<select class="form-control">
<option value="one">One</option>
<option value="two">Two</option>
<option value="three">Three</option>
<option value="four">Four</option>
<option value="five">Five</option>
</select>
How should assign those options for the {{ form.savgp }} variable?
Please let me know if I can provide any further information to explain my issue better.

I think its helpful to you
forms.py
class T3InputForm(Form):
savgp = SelectField('Selected Averaging Period', choices=[('one', "One"), ('two, "Two"),('three', "Three"), ('four, "Four"),('five',"Five")], [validators.Required()])
html
<div class="form-group col-md-6">
<p> <h4>{{ form.savgp.label}} : {{ form.savgp(class_="form-control") }} </h4></p>
</div>

Related

Problem with select and request.form.get and getlist in Flask

My problem is not that serious, just a little bit annoying. I have a dropdown menu and a list of values; however, my values resets themselves to the first option, and I would like for them to remain as the user selected them.
I have read from other sources that the solution is using getlist instead of get, but when I attempt to do it, I get the following error:
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
I have little experience working with flask and Jinga. I guess this must be a problem with the type of values, or some type of name or value that I need to fetch... I honestly have no idea. Any help will be appreciated.
Here is the video of how the flask is working with request.form.get, and here is the code that I have for that specific html view and the fragment of the app where I am requesting the data.
#app.route('/apuntual', methods = ['GET','POST'])
def apunt():
if request.method == 'POST':
# This is the button that I click on the video
if request.form.get('capturar') == 'capturar':
sample_rate_unf = request.form.get('f_muestreo')
samples_to_acq_unf = request.form.get('m_canal')
#Changing the values to int so I can work with them
sample_rate = int(sample_rate_unf)
samples_to_acq = int(samples_to_acq_unf)
#lots of more code in here
#
# Sending the output values to make the graph, and some other values to
# display in the html
return render_template('apuntual.html', fmax = fmax, tad = tad, imagen = datos)
<div class="col-md-2">
<form method="POST">
<h5 class="mb-1">Frecuencia de muestreo</h5>
<select name="f_muestreo">
<option value="2048">2048</option>
<option value="2560">2560</option>
<option value="3200">3200</option>
<option value="5120">5120</option>
</select>
<h5 class="mb-1">Muestras por canal</h5>
<select name="m_canal">
<option value="2048">2048</option>
<option value="4096">4096</option>
<option value="8192">8192</option>
</select>
<h5 class="mb-1">Captura instantánea</h5>
<p class="bs-component">
<input type="submit" class="btn btn-primary" name="capturar" value="capturar">
<input type="submit" class="btn btn-primary" name="borrar" value="borrar">
</p>
<p class=""bs-component>Frecuencia máxima de: {{ fmax }} Hz con TAD: {{ tad }} ms.</p>
</form>
</div>
One solution I can think of is to pass the selected option as a variable to the template and mark the selected option in the template. Here is a demo:
#app.route("/", methods=("GET", "POST"))
def demo():
options = (1, 2, 3, 4)
selected = None
if request.method == "POST":
selected = int(request.form.get("something"))
# Rest of the code goes here
return render_template("demo.html", options=options, selected=selected)
<form method="POST">
<select name="something">
{% for op in options %}
{% if selected == op %}
<option value="{{ op }}" selected>{{ op }}</option>
{% else %}
<option value="{{ op }}">{{ op }}</option>
{% endif %}
{% endfor $}
</select>
<input type="submit">
</form>
Notice that I put all the options as a tuple in the server code. One of reason is to avoid repetitions in the template code. It is also generally considered a bad practice to store data directly to the frontend code like what you are doing here. My demo is not perfect either. A better solution is to put all these options into a configuration file.

Populate textarea field using WTForms

As I understand it, if you want to populate a textarea you place the text between the textarea tags. However I am using WTForms. How can I pre-populate the form from views or in my template?
FORM
class ModuleSectionForm(FlaskForm):
title = StringField('Section Title', validators=[DataRequired()])
description = TextAreaField('Description', validators=[DataRequired()])
submit = SubmitField('Add Section')
VIEW
#modules.route('/update_section/<name>/<title>', methods=['GET', 'POST'])
def update_section(name, title):
form = ModuleSectionForm()
module = Module.objects(title=name).first()
section = None
for sect in module.sections:
if sect.title == title:
section = sect
#if form.validate_on_submit():
#save data
return render_template('modules/update_section.html', section=section, form=form)
TEMPLATE
<form method="post" action="{{ url_for('modules.update_section', name=name) }}">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.title.label(class="form-control-label") }}
{{ form.title(class="form-control", value=section.title) }}
</div>
<div class="form-group">
{{ form.description.label(class="form-control-label") }}
{{ form.description(class="form-control", default=section.description) }}
</div>
<div class="form-group">
{{ form.submit(class="btn btn-secondary shadow") }}
</div>
</form>
This can be done by just assigning the text to display beforehand (in the view, for instance).
Try edit your view (update_section) this way:
+ form.description.data = 'text you want to display'
And your template as follows:
- {{ form.description(class="form-control", default=section.description) }}
+ {{ form.description(class="form-control") }}
Mind you, there's an alternative way, which is to specify the placeholder attribute (but I guess is not what you want to do here).
Typically with WTForms (let's assume you're using Flask and Bootstrap here), you'll use the value attribute of the input to pre-populate a form field. Note that pre-populating a field is distinct from providing a 'placeholder', which is just an ephemeral hint. So usually we pre-populate like this:
<div class="form-group row">
<label for="form_subject" class="col-sm-2 col-form-label">Subject</label>
<div class="col-sm-7">
<input class="form-control" id="form_framework" name="form_framework"
value="{{ instruct.Subject }}">
</div>
</div>
With Flask and Bootstrap, the name attribute is required to pass the value back to the Controller upon submission, the value attribute is used to pre-populate the field from the object - in this case, our controller has passed in an object called instruct, which has an attribute Subject.
But you have to be aware that different kinds of inputs in WTForms have different attributes, and this is left as a fun challenge for the developer to figure out.
TextArea doesn't have a value attribute, so in order to pre-populate the field, you have to provide the value between the tags, like so (again, using Bootstrap here in case any of these tags are unfamiliar):
<div class="form-group row">
<label for="form_longish_text"
class="col-sm-2 col-form-label">Longish Text</label>
<div class="col-sm-7">
<textarea class="form-control" rows="3"
id="form_longish_text"
name="form_longish_text">{{ instruct.LongishText }}
</textarea>
</div>
</div>

Flask wtf-form override error message

Im using wtf-form for my password reset page but im having trouble with the generated error messages. I want to be able to either disable the error that wtf-form generates and use my own or be able to override them.
{% for e in form.password.errors %}
<span class="label label-danger">{{ e }}</span>
{% endfor %}
<form action="http://127.0.0.1:5000/setpassword" method=post>
<div class="form-group">
{{ form.csrf_token }}
{{ wtf.form_field(form.password, class='form-control', placeholder='Enter Password') }}
</div>
class PasswordForm(FlaskForm):
password = PasswordField('',validators=[pass_num, pass_small_lett, pass_big_lett, pass_special, pass_min])
confirm = PasswordField('',[validators.EqualTo('password', message='Not matching')])
submit = SubmitField('Send')
Don't use form_field() if you don't want its extra functionality. This will render just the input field:
{{ form.password(class='form-control', placeholder='Enter Password') }}
In your example the field's label is empty, but in case the field has a label, it must be printed as well. Bootstrap rules are:
Wrap labels and form controls in <div class="form-group"> (needed for optimum spacing)
Add class .form-control to all textual <input>, <textarea>, and <select elements
(source: https://www.w3schools.com/bootstrap/bootstrap_forms.asp)

Select a radio button in a jinja2 template

I have a jinja2 template with a simple user preferences form. The preferences are passed as a python dictionary into the render_template() function. Depending on a setting, I need to mark one of the radio buttons in a group as checked.
Q: How to do it cleanly, without much code repetition?
Below is my current solution. It works, but it's ugly, and with more than 2 radio buttons, it would soon become hard to manage. There is probably a better way.
I use two string variables (for the 2 radio btns). One of them will be empty, and the other will be set to 'checked', according to the preference setting:
{% if user_prefs['when_done'] == 'index' %}
{% set indexchecked = 'checked' %}
{% set backchecked = '' %}
{% else %}
{% set indexchecked = '' %}
{% set backchecked = 'checked' %}
{% endif %}
<!-- With more radio buttons here, this would be a mess! -->
And then I use these strings in the template:
<form action="{{ url_for('prefs') }}" method="post">
<fieldset>
<div class="radio text-left">
<p><strong>After completing a task:</strong></p>
<label>
<input type="radio" name="when_done" value="index" {{ indexchecked }}>
Return to homepage
</label>
<br/>
<label>
<input type="radio" name="when_done" value="task" {{ taskchecked }}>
Go to next task
</label>
</div>
<div class="form-group">
<button class="btn btn-default" type="submit">Update preferences</button>
</div>
</fieldset>
</form>
I would move the computation of indexchecked and backchecked from the template to the code. You can also use dictionary unpacking in order to pass less parameters to the render_template method.
do_index = user_prefs['when_done'] == 'index'
index_checked = 'checked' if do_index else ''
back_checked = '' if do_index else 'checked'
render_template('pages/something.html', form=some_form, index_checked=index_checked, back_checked=back_checked)
In order to reduce the number of passed parameters you can use a dict with unpacking:
template_parameters = dict(form=some_form, index_checked=index_checked, back_checked=back_checked)
render_template('pages/something.html', **template_parameters)

Django custom form template

I've a custom template to render my form in my Django website. This code renders the ChoiceFields:
<select id="{{ "id_"|add:field.html_name }}"
class="form-control"
name="{{ field.html_name }}">
{% for id, name in field.field.choices %}
<option value="{{ id }}"
{% if field.value|default_if_none:"" == id %}
selected="selected"
{% endif %}
>{{ name }}</option>
{% endfor %}
</select>
This code works perfectly, when the ChoiceField does not have an initial value. If I sumbit the form, but there's an error in the form (sp I get back to my form, and all the data is in there as I submitted it), the correct choice get the selected="selected" attribute, so it works perfectly fine.
But when I set the default value for the form in my Django view (correctly in the form's init() function, like this: self.fields['card_type'].initial = self.card.card_type_id), this stops working.
If I put manually somewhere in my template the form.value variable it displays the correct integer. In the id, name for-loop there's the same id value. These do not have any whitespace ot something else there. But the code does not works, does not equals to true the self.fields['card_type'].initial = self.card.card_type_id condition.
If I modify the previous code, to print the id, and the field.value instead of the {{name}}, like this: ({{id}}-{{ field.value }}), the following HTML code'll be generated:
<select id="id_card_type" class="form-control" name="card_type">
<option value="3">(3-2)</option>
<option value="2">(2-2)</option>
<option value="1">(1-2)</option>
</select>
There's the id, which is 1, 2, and 3! There's the form.value, which is 2! But there isn't any selected="selected"... Why not working?
Thanks!

Categories

Resources