I don't want to use javascript as its probably just a one-off usecase. Here's my code and the problem statement follows.
# Form class
class ManageContainers(FlaskForm):
stop_container = SubmitField('stop')
# HTML Render
{% for i in containers %}
<tbody>
<tr>
<th scope="row">{{ i.short_id }}</th>
<td>{{ i.name }}</td>
<td>{{ i.image.tags }}</td>
<td>{{ i.ports }}</td>
<td>
<form action="#" method="post">
{{ form.stop_container() }}
</form>
</td>
</tr>
</tbody>
{% endfor %}
# Actual app route
#app.route('/list-containers', methods=['POST','GET'])
def index():
containers_list = client.containers.list(all=False)
form = ManageContainers()
if form.is_submitted():
print("stop the container")
delete_container(HOW_TO_GET_THIS?)
return redirect(url_for('index'))
return render_template('index.html', containers=containers_list, form=form)
def delete_container(container_id):
container = client.containers.get(container_id)
container.stop()
container.remove()
I am using docker sdk for python and basically trying to stop / start containers. I am stuck where I want to use that button that says "stop" which will delete the container.
Important - It is actually calling a function like this because I can see "stop the container" in my console. But I don't know how to pass the ID (i.id) of the container while the function is getting called.
Here's a screenshot of what the page actually looks like to save you sometime.
SCREENSHOT
Since you are not taking any user input for any fields, I don't think, it's related to WTForms.
You can try the below steps:
Create a route for stopping the container:
#app.route('/stop-container/<id>', methods=['POST'])
def stop_container(id):
delete_container(id)
return redirect(url_for('index'))
Change your form to:
<form action={{ url_for('stop_container', id=i.short_id) }} method='post'>
{{ form.stop_container() }}
</form>
Related
I have following issue with validate_on_submit() method in my Flask application.
Quick overview what I have:
My form class is created dynamically based on input:
class EditUsers(FlaskForm):
submit = SubmitField('Submit')
def form_builder(roles_list):
class EditUsersForm(EditUsers):
pass
for role in roles_list:
if isinstance(role, tuple) and len(role) == 5:
if not role[2] and role[3]:
setattr(EditUsersForm, f'{role[0]}_{role[4]}_bool', BooleanField(label=role[0]))
setattr(EditUsersForm, f'{role[0]}_{role[4]}_date', SelectField('Expiration Period', choices=choices))
else:
raise IncorrectType
IncorrectType is custom Exception i prepared and choices are created using datetime in same file (This is irrelevant so I am not including it in code).
My route in flask app (simplified):
#### EDIT: I pasted the wrong route, POST and GET Methods are included###
#edit_users.route('/users', methods=['GET', 'POST'])
def users():
... # Some code there
form = form_builder(roles_to_form)
print(form.is_submitted())
print(form.validate())
print(form.validate_on_submit())
return render_template('edit_user.html',
data_dict=data_dict, # in data_dict i pass form fields names and some other data
form=form,
)
My template:
<!-- Some usual stuff goes there css js etc -->
<div >
<form class="form form-horizontal" method="post" role="form" style="margin: auto; text-align: center; width: 40%;">
{{ form.csrf_token() }}
<table class="table" align="center">
<thead>
<th>Role</th>
<th>Expiration Date</th>
<th>Give Role?</th>
</thead>
{% for field in data_dict['id_list'] %}
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="3" align="center">{{ form.submit() }}</td>
</tr>
</table>
</form>
</div>
What is my issue?
When I am hitting my Submit button, is_submitted() returns True but validate() does not, thus I cannot use typical if form.validated_on_submit() as it returns False even when I submit my form.
I dig a little and spot something unusual. I used protected members of form attributes, to check whatever validate() function sees as input:
for name in form._fields:
print(name)
inline = getattr(form.__class__, 'validate_%s' % name, None)
print(inline)
The output (don't mind names of fields):
submit
None
Engineer_2_bool
None
Engineer_2_date
None
Operator_3_bool
None
Operator_3_date
None
Supervisor_4_bool
None
Supervisor_4_date
None
Guest_5_bool
None
Guest_5_date
None
csrf_token
None
I don't know why my form does not have validate_{name} attributes.
I made a working example from your code, and I ended up with the problem you described.
If I reversed well your code, it seems that form[field[1]] is your BooleanField or SelectField. So to render it in a template your have to use form[field[1]]() (can use render_field as well).
So :
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]] }}
</td>
<td align="left">{{ form[field[0]] }}</td>
</tr>
corrected into :
<tr>
<td align="left">{{ field[2] }}</td>
<td align="left">
{{ form[field[1]]() }}
</td>
<td align="left">{{ form[field[0]]() }}</td>
</tr>
https://flask.palletsprojects.com/en/1.1.x/patterns/wtforms/#forms-in-templates
As you are using a FlaskForm, in your template you have to replace {{ form.csrf_token() }} by {{ form.csrf_token }}
https://flask-wtf.readthedocs.io/en/stable/csrf.html#html-forms
[EDIT Does not make any difference]
[Edited after w8eight's comment]
The route is not authorized for POST (and the form makes a POST request as seen in <form class="form form-horizontal" method="post" [...].
So you have to change : #edit_users.route('/users', methods=['GET']) to #edit_users.route('/users', methods=['GET','POST'])
The problem was with type of data passed to choices of SelectField. I passed list of tuples with datetime.datetime to it. When changed type to str everything works smoothly.
On my ListView page, I need to be able to loop through a list of statuses that come from my views.py.
I can do it on the DetailView, but I am struggling with the same process on the ListView.
I have searched and searched and can not seem to find help with my specific problem.
Working code for DetailView:
managers.py
class ICCRequestManager:
...
#property
def statuses(self):
""" Current status of ICC request """
if not self.is_submitted:
yield "Created, not yet submitted"
if self.is_submitted and not self.legal_signoff_status:
yield "Waiting for security review"
if self.is_submitted and not self.security_signoff_status:
yield "Waiting for legal review"
if self.legal_signoff_status and self.security_signoff_status:
yield "Fully reviewed, ICC team is working"
views.py
from . import managers
...
class ICCRequestMixin:
""" Mixin for the request model """
model = models.ICCRequest
context_object_name = 'icc_request'
class ICCRequestDetailView(LoginRequiredMixin, ICCRequestMixin, DetailView):
""" View to see the current status of an ICC request """
template_name = "icc/request_status.html"
def get_context_data(self, **kwargs):
""" Add required context """
context = super().get_context_data(**kwargs)
context["icc_request_manager"] = managers.ICCRequestManager(self.object)
return context
request_status.html
<ul>
{% for value in icc_request_manager.statuses %}
<li>{{ value }}</li>
{% endfor %}
</ul>
The above displays a simple bulleted list as expected.
Problem code:
views.py
class ICCRequestListView(LoginRequiredMixin, ICCRequestMixin, ListView):
""" View to list requests """
template_name = "icc/request_list.html"
paginate_by = 10
def get_context_data(self, **kwargs):
""" Add required context """
context = super().get_context_data(**kwargs)
context["icc_request_manager"] = managers.ICCRequestManager(self.object_list)
return context
Working portion:
request_list.html
<table class="table table-striped table-bordered">
<tr>
<th scope="col">ICC #</th>
<th scope="col">Company</th>
<th scope="col">Brief Description</th>
<th scope="col">LOB</th>
<th scope="col">Requester</th>
<th scope="col">Status</th>
</tr>
{% for request in icc_request %}
<tr>
<th scope="row">{{ request.id }}</th>
<td>{{ request.company_name}}</td>
<td>{{ request.summary }}</td>
<td>{{ request.owner_area }}</td>
<td>{{ request.requested_on_behalf_of }}</td>
<!--- I want the statuses list here --->
</tr>
</table>
Attempt to loop through like on the detail page:
...
<!--- I want the statuses list here --->
<td>
<ul>
{% for status in request.icc_request_manager.statuses %}
<li>
{{ status }}
</li>
{% endfor %}
</ul>
</td>
{% endfor %}
</tr>
</table>
This does not give an error, but it does not display anything (while the request_status.html page does).
Only error is the following linting error on the get_context_data:
"Parameters differ from overridden 'get_context_data' method."
So I figured it out.
1) get_context_data on the ListView has nothing to do with it. Not needed.
2) I needed to change from yields to returning a list:
def statuses(self):
""" Current status of icc request """
statuses = list()
if not self.is_submitted:
statuses.append("Created, not yet submitted")
if self.is_submitted and not self.legal_signoff_status:
statuses.append("Waiting for security sign off")
if self.is_submitted and not self.security_signoff_status:
statuses.append("Waiting for security review")
if self.legal_signoff_status and self.security_signoff_status:
statuses.append("Fully reviewed, ICC team is working")
return statuses
3) Added a method to the model:
#property
def get_icc_request_manager(self):
""" Get the manager for this one request """
# Needed to import managers inside the method to prevent Circular import problem
import icc.managers
mgr = icc.managers.ICCRequestManager(icc_request=self)
return mgr
4) Used with:
<table class="table table-striped table-bordered">
<tr>
<th scope="col">ICC #</th>
<th scope="col">Company</th>
<th scope="col">Brief Description</th>
<th scope="col">LOB</th>
<th scope="col">Requester</th>
<th scope="col">Status</th>
</tr>
{% for request in icc_request %}
{% with request_manager=request.get_icc_request_manager %}
<tr>
<th scope="row">{{ request.id }}</th>
<td>{{ request.company_name}}</td>
<td>{{ request.summary }}</td>
<td>{{ request.owner_area }}</td>
<td>
{% if request.requested_on_behalf_of %}
{{ request.requested_on_behalf_of }}
{% elif request.requested_by %}
{{ request.requested_by }}
{% else %}
{% endif %}
</td>
<td>
<ul>
{% for status in request_manager.statuses %}
<li>
{{ status }} test
</li>
{% endfor %}
</ul>
</td>
{% endwith %}
{% endfor %}
</tr>
</table>
I am trying to develop a WTForm where users can select one or many part numbers in a table. After the user enters a part number I want to show the master data associated to it (right next to the part number). Such as weight, price etc., rendered in the same template. An SQLIte Table is holding the master data for all part numbers.
At the end, the intention is to sum up the values (price, weight) in a seperate line for the part numbers entered in the WTForm list.
All I find in documentation for WTForm processing is related to "on_submit". But users will never submit the form.
I believe I do not have the right implementation approach right now. First, my current coding is not updating the values in the form, second I dont want to have a submit button in order to see the content_values.
class kundenanalyse_items(FlaskForm):
ka_fm = SelectField('Futtermittel', choices=[('a', 'a Wert'), ('b', 'b
Wert'), ('17', 'irgendwas')])
ka_fm_menge = IntegerField('g/Tag (Tagesplan) bzw. g/Woche (Wochenplan)')
ka_fm_content01 = IntegerField('ka_fm_content01')
ka_fm_content02 = DecimalField('ka_fm_content02')
#app.route('/kundenanalyse', methods=['GET', 'POST'])
def kundenanalyse():
ka_line01 = kundenanalyse_items()
if request.method == 'POST':
flash('Daten an DB gesendet {} : {} + {} :
{}'.format(ka_line01.ka_fm.data, ka_line01.ka_fm_menge.data,
ka_line02.ka_fm.data))
ka_line01.ka_fm_content01.data = '100' * ka_line01.ka_fm_menge.data
ka_line01.ka_fm_content02.data = '200' * ka_line01.ka_fm_menge.data
return redirect(url_for('kundenanalyse'))
return render_template('kundenanalyse.html', ka_line01=ka_line01)
kundenanlyse.html:
{% extends 'layout.html' %}
{% block body %}
{% for message in get_flashed_messages() %}
<div class=flash style="color: orangered;">
{{ message }}
</div>
{% endfor %}
<form method="POST", action="/kundenanalyse")>
<div class="tg-wrap"><table class="tg" style="undefined;">
<tr>
<th class="tg-wh92">{{ ka_line01.ka_fm.label }}</th>
<th class="tg-wh92">{{ ka_line01.ka_fm_menge.label }}</th>
<th class="tg-wh92">{{ ka_line01.ka_fm_content01.label }}</th>
<th class="tg-wh92">{{ ka_line01.ka_fm_content02.label }}</th>
</tr>
<tr>
<td class="tg-e3ma">{{ ka_line01.ka_fm }}</td>
<td class="tg-sh4c">{{ ka_line01.ka_fm_menge }}</td>
<td class="tg-hkz2">{{ ka_line01.ka_fm_content01 }}</td>
<td class="tg-hkz2">{{ ka_line01.ka_fm_content02 }}</td>
</tr>
</table>
</div>
<input type="submit" value="Submit">
</form>
{% endblock %}
thank you in advance.
Michael
I was trying to iterate zip list in jinja2 and display values in HTML table but failed at every single try with a blank page, however, I can display values in the Unordered list like as follows.
<ul>
{% for bus, info in jnjbus_info %}
<li>{{bus}}</li>
<li>{{info}}</li>
{% endfor %}
</ul>
This is my flask/function where I passing values to template:
#app.route('/busses')
def busses():
bus_type = ['AC', 'NON-AC', 'Sleeper', 'NON-Sleeper']
bus_info = ['1010', '2020', '3030', '4040']
return render_template('busses.html', jnjbus_info=zip(bus_type, bus_info))
I'm rendering template called busses.html
Here's the script:
<table style="width:100%">
<tr>
<th>Bus Type</th>
<th>Bus Information</th>
</tr>
{% for bus, info in jnjbus_info %}
<tr>
<td>{{bus}}</td>
<td>{{info}}</td>
</tr>
{% endfor %}
</table>
you don't have the
<tbody> </tbody>
tag in your page i added it and it works:
<table style="width:100%">
<tr>
<th>Bus Type</th>
<th>Bus Information</th>
</tr>
<tbody>
{% for bus, info in jnjbus_info %}
<tr>
<td>{{bus}}</td>
<td>{{info}}</td>
</tr>
{% endfor %}
</tbody>
</table>
here is how it looks like :
The code provided in this question is bug-free, however, the problem caused by port=5000 and may be browser cache as well. While tackling with the bug I have written a similar script py and html and screen shot .and ran on port=8888 which works like charm. Note : consider running the same application on different ports and clearing browser cache.
I made a Flask WTForm to allow my user to confirm a series of informations. I show the form pre-filled with the informations, and the user can modify it and submit the form. However, even when no error occurs (form.errors is empty), form.validate() always returns False. What am I doing wrong?
App
#app.route('/validation', methods=['GET', 'POST'])
def validation():
# initiate form
item = session.get('item', None)
form = AttrForm(request.form)
# pre-fill validation
form.price.data = item['price']
form.surface.data = item['surface']
form.rooms.data = item['rooms']
form.year.data = item['year']
# get area options and pre-fill
options = get_options()
form.subarea.choices = options
form.subarea.data = get_choice(options, item['subarea'])
# catch errors
if not form.validate_on_submit():
display = {'url': False, 'fill' : True, 'valid' : False}
return render_template('index.html', display=display, form=form)
# set new values
item['price'] = request.form['price']
item['surface'] = request.form['surface']
item['rooms'] = request.form['rooms']
item['year'] = request.form['year']
item['subarea'] = request.form['subarea']
session['item'] = item
return redirect(url_for('results'))
HTML
{% block attr_form %}
<h2>Attribute Form</h2>
<form action="/validation" method="POST">
<table>
<tr>
<td>Loyer (hors charges)</td>
<td>{{ render_field(form.price) }}</td>
</tr>
<tr>
<td>Surface</td>
<td>{{ render_field(form.surface) }}</td>
</tr>
<tr>
<td>Année de construction</td>
<td>{{ render_field(form.year) }}</td>
</tr>
<tr>
<td>Nombre de pièces</td>
<td>{{ render_field(form.rooms) }}</td>
</tr>
<tr>
<td>Quartier administratif</td>
<td>{{ render_field(form.subarea) }}</td>
</tr>
</table>
<input type=submit value="Je valide"></p>
</form>
{% endblock %}
Helpers
{% macro render_field(field) %}
<dd>{{ field }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<ul>{{ error }}</ul>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
Form
class AttrForm(FlaskForm):
price = DecimalField('price', validators=[DataRequired()])
subarea = SelectField('subarea', coerce=int)
surface = DecimalField('surface', validators=[DataRequired()])
year = IntegerField('year', validators=[DataRequired()])
rooms = IntegerField('rooms', validators=[DataRequired()])
First make sure your defined a SECRET_KEYin your flask config. You need to do this in order for CSRFprotection to work. http://flask-wtf.readthedocs.io/en/stable/csrf.html
Second, add the csrf token to your html form by inserting form.hidden_tag.
<form action="/validation" method="POST">
{{ form.hidden_tag() }}
<table>
<tr>
...
See http://flask-wtf.readthedocs.io/en/stable/quickstart.html for details.