Hi I am currently having a project with python flask on Raspberry Pi.
My project will include some physical component such as sensor and motor. Now I like to perform some task when a 'start' button from the web page is pressed, and that function which the button called will keeps looping until another 'stop' button is pressed. Within the looping task, result will produce and I will need to update the result back to the web page at the same time which not stopping my looping task.
Below is what I'd tried, I am trying a solution with "context_processor" but I am not very understand with the working and not sure is that the true solution which solve my problem.
{% block body %}
<form method="POST">
<label style="font-size: 20px;"> A : {{ output_img }}</label>
<br/>
<label style="font-size: 20px;">B : {{ output_value }}</label>
<br/>
<button class="btn btn-lg btn-success" type="submit" name="submit_button" value="StartBtn">Start</button>
<button class="btn btn-lg btn-success" type="submit" name="submit_button" value="StopBtn">Stop</button>
</form>
{% endblock %}
#app.route('/about', methods=['GET', 'POST'])
def about_page():
if request.method == "POST":
if "user" in session:
user = session["user"]
else:
return redirect('/')
if request.form['submit_button'] == 'StartBtn':
print("Start Pressed")
testloop()
return redirect('/about')
if request.form['submit_button'] == 'StopBtn':
print("Stop Pressed")
return redirect('/about')
else:
return render_template('about.html', message="")
From above python code, when the "Start Btn" is pressed, my code will jump into a function which shown below and keep looping over that function, after that when the user pressed "Stop Btn" is pressed, the looping function will stop.
def testloop():
global output_value, output_img
a = 0
for i in range(10):
sleep(3)
output_value = str(a) + ' value'
output_img = str(a) + ' img'
print(a)
a = a + 1
and last but not least, this is my "context_processor" code.
output_value = ''
output_img = "/static/pics/noimage.jpg"
#app.context_processor
def context_processor():
global output_value, output_img
return dict(value=output_value, outputimg=output_img)
I hope I had explain my problem clearly. Thanks for the help!
Update, below picture may help me explain clearly about my problem.
As what I understand, when the user pressed a button, the server will run for that particular function, after returning the result to the client site (webpage) the server will stop running and wait for another command. How can I get one command from the button and keeps the server running in a loop and pushing the result time to time back to the client site within the looping?
Related
I'm testing API CRUD with browsable web API flask implementation, but the browser seems to send unexpected requests to the API.
Here is the code I'm testing :
from flask import request, url_for
from flask_api import FlaskAPI, status, exceptions
app = FlaskAPI(__name__)
notes = {
0: 'do the shopping',
1: 'build the codez',
2: 'paint the door',
}
def note_repr(key):
return {
'url': request.host_url.rstrip('/') + url_for('notes_detail', key=key),
'text': notes[key]
}
#app.route("/", methods=['GET', 'POST'])
def notes_list():
"""
List or create notes.
"""
if request.method == 'POST':
note = str(request.data.get('text', ''))
idx = max(notes.keys()) + 1
notes[idx] = note
return note_repr(idx), status.HTTP_201_CREATED
# request.method == 'GET'
return [note_repr(idx) for idx in sorted(notes.keys())]
#app.route("/<int:key>/", methods=['GET', 'PUT', 'DELETE'])
def notes_detail(key):
"""
Retrieve, update or delete note instances.
"""
if request.method == 'PUT':
note = str(request.data.get('text', ''))
notes[key] = note
return note_repr(key)
elif request.method == 'DELETE':
notes.pop(key, None)
return '', status.HTTP_204_NO_CONTENT
# request.method == 'GET'
if key not in notes:
raise exceptions.NotFound()
return note_repr(key)
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
When I tried to delete a specific note given its id (key) from the example notes list, the browser sends a POST method instead of DELETE, which is not supported by the route.
The base render template is located here where you can see DELETE button statement at line 104.
I edited the library code and move the form method there from POST to DELETE thinking it could solve the problem this way :
104 {% if 'DELETE' in allowed_methods %}
105 <form class="button-form" action="{{ request.url }}" method="DELETE" class="pull-right">
106 <!-- csrf_token -->
107 <input type="hidden" name="_method" value="DELETE" />
108 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109 </form>
110 {% endif %}
But not, the browser is now sending a GET request with the query string _method=DELETE, instead of DELETE request/method.
Everything is OK when sending request to the API using curl
Can one of you guys with good flask html rendering skills check this out and test on its side?
Never seen form with method=DELETE. MDN documentation says it should be either a GET or POST. Also see this old stackoverflow question that also says it is not supported in forms.
The template you're referencing is for an API so it seems to me that they are supporting direct calls to the API (such as with curl or Postman which would support PUT, DELETE, etc) and calls via forms (which would only be GET or POST)
You should keep your original code where method = POST and add POST as a method for def notes_detail i.e. you should have
#app.route("/<int:key>/", methods=['GET', 'POST', 'PUT', 'DELETE'])
def notes_detail(key):
I abandoned the effort of making the browser sending DELETE requests, and dealed with form hiden inputs, keeping POST as the form's method
104 {% if 'DELETE' in allowed_methods %}
105 <form class="button-form" action="{{ request.url }}" method="POST" class="pull-right">
106 <!-- csrf_token -->
107 <input type="hidden" name="_method" value="DELETE" />
108 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109 </form>
110 {% endif %}
im following a tuto on how ajax work on Django, its my first time with ajax and im facing a little problem ,the data insertion is working but the success ajax dont redirect corectly, and thank you for the help
this the code
views.py :
class exo(View):
def get(self, request):
form = ExerciseForm()
tasks = task.objects.all()
context = {
'form': form,
'tasks': tasks
}
return render(request, 'coach/test.html',
context=context)
def post(self, request):
form = ExerciseForm()
if request.method == 'POST':
form = ExerciseForm(request.POST)
print(form)
if form.is_valid():
print('adding task', form)
new_exrercise = form.save()
return JsonResponse({'task': model_to_dict(new_exrercise)}, status=200 )
else:
print('not adding task')
return redirect('exo')
ajax function :
$(document).ready(function(){
$("#addExercise").click(function() {
var serializedData = $("#TaskForm").serialize();
$.ajax({
url: $("TaskForm").data('url'),
data : serializedData,
type: 'post',
success: function(response) {
$("#taskList").append('<div
class="card"><div class="card-body">'+ response.task.name
+'<button type="button" class="close float-right"> <span
aria-hidden="true">×</span></button></div></div>');
}
})
});
});
html content :
<form class="submit-form" method="post" id="TaskForm"
data-url="{% url 'session' %}">
{% csrf_token %}
<div class="form-group">
{% for field in form %}
<div style="margin-bottom: 2rem;"></div>
{{field}}
{% endfor %}
<div style="margin-bottom: 2rem;"></div>
<button type="submit" class="btn btn-success dropdown-toggle " id="addExercise">Confirm</button>
</div>
</form>
this is what i get (i get an object and nothing else )
output image
when i comeback to the page exo the insertion is done
and the console show me that the post is sending well :
[21/Dec/2020 22:25:38] "POST /coach/exo/ HTTP/1.1" 200 43
The problem is that your button:
<button type="submit" class="btn btn-success dropdown-toggle " id="addExercise">Confirm</button>
will submit the form, and not (only) by the AJAX call, but simply by the fact that you made it a submit button. This thus means that the browser will encode the form and make a POST request with that form, and then the browser will render the response.
You thus should remove the type="submit" part from the button:
<button class="btn btn-success dropdown-toggle " id="addExercise">Confirm</button>
I remove the submit type and nothing was working , the button wasnt posting anything to ajax then i switch my view class into a function and it work i really dont know why there is the view
def addsession(request):
template = loader.get_template('coach/addexercise.html')
exercises = exercise.objects.all()
# context = {'exercises': exercises}
UpperBody = exercise.objects.filter(category__name="Upper Body")
LowerBody = exercise.objects.filter(category__name="Lower Body")
FIIT = exercise.objects.filter(category__name="FIIT")
LIIT = exercise.objects.filter(category__name="LIIT")
form = ExerciseForm()
tasks = task.objects.all()
context = {'exercises': exercises,
'UpperBody': UpperBody,
'LowerBody': LowerBody,
'FIIT': FIIT,
'LIIT': LIIT,
'form': form,
'tasks': tasks
}
form = ExerciseForm()
if request.method == 'POST':
form = ExerciseForm(request.POST)
print(form)
if form.is_valid():
print('adding task', form)
new_exrercise = form.save()
return JsonResponse({'task': model_to_dict(new_exrercise)}, status=200 )
else:
print('not adding task')
return redirect('session')
return render(request, 'coach/addexercise.html', context=context)
and thank u guys for your answers i really didnt give attention to the input type of button
I have a table in which I populate with data from the database. Some of this I have an extra feature of the delete button. But I can't understand how I get that what delete button is pressed in django
<tbody>
{% for i in list %}
<tr>
<td>{{i.id}}</td>
<td>{{i.reason}}</td>
<td>{{i.starting_date}}</td>
<td>{{i.ending_date}}</td>
<td>{{i.accept_or_not}}</td>
{% if i.accept_or_not == 'pending'%}
<td><input type="button" name="data_delete_{{i.id}}" value="delete"></td>
{%endif%}
</tr>
{% endfor%}
</tbody>
def profile_view(request):
if 'data_delete_id' in request.POST:
# i don't know how to determine the id in the button
This might be straight-forward. You can get the name of the button first and then parse the id such as:
def profile_view(request):
delete_button_id = ""
for name in request.POST.values():
if name.startswith('data_delete_'):
delete_button_id = int(name.split('_')[2])
break
# you now have the id in your delete_button_id variable
According to Django Docs for HttpRequest.POST your python code has to work as expected because POST returns a dict like Object.
def profile_view(request):
if 'data_delete_id' in request.POST:
But in your template you are using <input type="button" .
This will only trigger a client side operation. If you are not having any JS code which you have not shown here, I would say you need to use <input type="submit" instead.
I would also recommend to use <button type="submit" instead of input because from a semantic perspective it is more accurate.
I have a tuple list and wanted to delete or add tuples in it depending on what button has been pressed. Adding tubles is functioning fine but my problem is, that for some reason if Im clicking on the button to delete a tuple, the list resets back the time to the state before the delete happened.
For example I have a list:
ctestformat = [('sung', 4, 15), ('ren', 3, 27), ('lexe', 4, 39)]
after deleting the number 15 I get:
ctestformat = [('ren', 3, 27), ('lexe', 4, 39)]
But after getting another POST request to delete or add, the list resets to the first state as if nothing got deleted
Here is my view to add and delete tuple depending on which button was clicked:
def editorstart(request, ctestformat=[]):
if request.method == 'POST':
"""If clicked on create gap button, create a new gap and put it in ctestformat"""
if 'create_gap' in request.POST:
selectedgap = request.POST['sendgap']
startindex = int(request.POST['startpoint'])-13
ctestformat.append((selectedgap, len(selectedgap), startindex))
ctestformat.sort(key=operator.itemgetter(2))
"""if clicked on deletegap, delete the gap from ctestformat"""
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
test = [t for t in ctestformat if t[2] != int(deleteindex)]
ctestformat = test
# This function doesnt change anything to ctestformat
modifiedtext = createmodifiedtext(ctestformat)
return render(request, 'editor_gapcreate.html', {"text": modifiedtext, 'ctestformat': ctestformat})
If you have any other questions, just ask :)
EDIT:
added return in my view
my template:
{% extends "base_generic2.html" %}
{% block content %}
<form action="editorgapcreate" id=create method="POST">
<input type="hidden" name="sendgap" id="sendgap">
<input type="hidden" name="startpoint" id="startpoint">
<script src="../static/textselector.js"></script>
<div id="thetext" onmouseup="getSelectionText()">
<h1>{{ text|safe }}</h1>
</div>
{% csrf_token %}
<p></p>
<b>Your current selected gap:</b>
<p id="currentgap"></p>
<input type="hidden" name="text" id="text" value="{{ text }}">
<button type="submit" name="create_gap" id="gapcreate">Create gap</button>
</form>
{% endblock %}
Using a mutable value for a default argument in Python (a list in this case) is not normally a good idea. The list is created once when the function is defined, which means any changes you make to it are visible in subsequent function invocations. However, it seems as though this may be intended in your case.
The reason why you're not seeing the list change, is that the assignment you're making ctestformat = test after filtering out an item has no effect. You need to mutate the original list rather than reassigning, by first finding the index of the item within that list, and then using pop() to remove it. For example:
elif 'deletegap' in request.POST:
deleteindex = request.POST['deletegap']
for i, t in enumerate(ctestformat):
if t[2] == int(deleteindex):
ctestformat.pop(i) # Modify original list
break
...
I would still recommend not using a mutable default argument to achieve this. If you need to share data across requests, you'd be better to use a cache or a database, or possibly session state, depending upon your application requirements.
I have a problem with a checkbox in a form
<form id="form" action="/findPkgInstalled" role="form" method = "POST">
<div class="input-group col-xs-4">
<input type="text" name="pkgSearch" placeholder="Ricerca applicazione non installate..">
<div class="input-group-btn">
<button class="btn btn-default" type="submit"><i class="glyphicon glyphicon-search"></i></button>
</div>
</div>
<h6><input type="checkbox" name="filterName" checked>Filtra per nome</h6>
</form>
while in Python I have:
#app.route('/findPkgInstalled', methods=['POST'])
def findPkgInstalled():
error = None
pkg = request.form['pkgSearch']
if not pkg:
flash(u'Operazione errata, impossibile ricercare stringa vuota','warning')
return redirect(url_for('listInstalled'))
else:
if request.form['filterName'] is 'on':
appFound = aptsearch(pkg,True)
return render_template('find-pkg-not-installed.html', appFound = appFound)
else:
appFound = aptsearch(pkg,False)
return render_template('find-pkg-not-installed.html', appFound = appFound)
return redirect(url_for('listInstalled'))
box = request.form['filterName']
but this does not work. The error reported is 400 Bad Request. How can I do? Can you help me, please?
This error means that you're trying to fetch an object from your post request using incorrect key. For example if you uncheck filterName checkbox - this will cause this error:
request.form['filterName']
My advises:
0) Always check your post body to know what keys you can use to retrieve values from this body.
1) Use
request.form.get('filterName')
instead of
request.form['filterName']
because .get() returns None is there is no such key instead of throwing an exception inside flask that leads to 400 error
2) Use
request.form.get('filterName') == 'on'
instead of
request.form['filterName'] is 'on'
because is returns True if two variables point to the same object in memory. I'm not sure if you already have 'on' object in process memory