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
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 %}
I have the following views:
def device_port(request):
devices = Device.objects.all()
if request.method == "POST":
selected=request.POST.get('device')
devices = Device.objects.get(pk=selected)
tablename = 'dev_interface_'+selected
print("tablename: " +tablename)
cursor=connection.cursor()
cursor.execute(f"SELECT interface FROM {tablename} WHERE id >=2")
righttable = cursor.fetchall()
return redirect('/device/port/selected',{'devices':devices, 'selected': selected, 'righttable':righttable} )
return render(request, 'interface/device_port.html',{'devices':devices})
def device_port_selected(request, pk):
if request.method == "POST":
job = JobForm(request.POST)
device = devices.hostname
print(devices)
#job.associateddevice = devices.hostname
try:
selection=request.POST.get('portrange')
except:
selection = ""
messages.warning(request, "Please select the ports")
print(selection)
#job.associatedinterface = selection
return render(request, 'interface/device/port/selected/'+device+'.html',{'devices':devices, 'righttable':righttable} )
return render(request, 'interface/device_port_selected.html',{'devices':devices, 'selected': selected, 'righttable':righttable} )
urls.py
urlpatterns = [
path('', views.home, name='interface-home'),
path('device/', DeviceListView.as_view(), name='interface-device'),
path('device_edit/<int:pk>/', views.device_edit, name='device-edit'),
path('device_delete/<int:pk>/', views.device_delete, name = 'device-delete'),
path('device_add/', views.device_add, name='device-add'),
path('device/port/', views.device_port, name='device-port'),
path('device/port/selected/', views.device_port_selected, name='device-port-selected'),
path('device/routeport/', views.device_routeport, name='device-routeport'),
path('interface/', views.interface_list, name='interface-list')
]
device_port.html
<form method="POST">
<div class="form-row align-items-center">
<div class="col-md-5 my-1">
{% csrf_token %}
<label for="Hostname">Hostname</label>
<div class="input-group">
<select id = "list" class="custom-select mr-sm-2" onchange="getSelectValue();">
<option selected>Select</option>
{% for device in devices %}
<option value={{device.id}}>{{device.hostname}}</option>
{%endfor%}
</select>
<div class="input-group-append">
<button class="btn btn-outline-secondary" type="submit">Go</button>
</div>
</div>
</div>
</div>
<input type ="text" name="device" id= "txtvalues" style="display:none">
</form>
So there are 2 page I am dealing with over here (/device/port and /device/port/selected). In this first page /device/port, user is required to pick a value from the drop down box and press the button Go. From here, it is intended to go to the next page which is /device/port/selected with the selected value in the first page pass to the next page.
But with the following code, I receive the error of
device_port_selected() missing 1 required positional argument: 'pk'
when moving from the first page to the next page.
You can't pass a context dictionary to a redirect. The second argument should be the URL arguments, not a context. So change the following line:
return redirect('/device/port/selected',{'devices':devices, 'selected': selected, 'righttable':righttable} )
to
return redirect('device-port-selected', pk=selected)
Note that it is better to use the name of the URL (i.e. device-port-selected) instead of the whole path as you can change paths in the future without affecting the rest of your code.
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?
I'm making an app to save mileage of a truck per state. I've already passed required data to my view, and then I thought to change my urls to more logical. And after that I faced a problem.
I don't know what should be instead of "unit.unit_number", and it is required, in my html file for it to work correctly. I didn't find anything that could explain how to deal with it.
If I try to access mywebsite.com/core/units/1/locations/add/ I get next error message:
"NoReverseMatch at /core/units/1/locations/add/"
But if I put just a number (1 for example) instead of "unit.unit_number" it loads the page normally, but I get an error after trying to post the data:
"TypeError at /core/units/1/locations/add/
_reverse_with_prefix() argument after * must be an iterable, not int"
<form action="{% url 'Core:unit_add_location' unit.unit_number %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="container">
<div class="inner-container border-full">
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
<input type="submit" class="button button-submit" name="count-ifta" value="Save">
<div class="inner-container border-top-bottom">
<table id="myTable" name="state-miles-data">
<thead>
<th class="text-blue">State</th>
<th class="text-blue">Miles</th>
</thead>
</table>
<br>
</div>
</form>
<button type="button" class="button button-normal" onclick="AddRow()">Add</button>
</div>
</div>
def unit_data(request, unit_number):
return HttpResponse(unit_number)
def unit_add_location(request, unit_number):
if "GET" == request.method:
return render(request, 'Core/add_location.html')
elif "POST" == request.method:
states_request = request.POST.getlist('states')
miles_request = request.POST.getlist('miles')
return HttpResponseRedirect(reverse('Core:unit_data', args=(unit_number)))
urlpatterns = [
path('units/', views.units_all, name = 'units_all'),
path('units/<int:unit_number>/', views.unit_data, name = 'unit'),
path('units/<int:unit_number>/locations/', views.unit_locations, name = 'unit_locations'),
path('units/<int:unit_number>/locations/add/', views.unit_add_location, name = 'unit_add_location'),
path('units/<int:unit_number>/locations/<int:report_id>', views.unit_location, name = 'unit_location'),
]
So the thing I want to make is post the data and redirect to url "mywebsite.com/units/1/locations/" that is processed by the view "unit_locations"
To the second part about the iterable: (reverse('Core:unit_data', args=(unit_number))) <<< you want (unit_number,) which is a tuple not an int. Spot the , it is important.
I'm trying to send my login with :
'submit form': function(event) {
event.preventDefault();
event.stopPropagation();
var loginRequest = {
username: event.target.loginUsername.value.toLowerCase(),
password: event.target.loginPassword.value,
};
var callback = function(response) {
Session.set('showLoading', false);
};
Session.set('showLoading', true);
Accounts.callLoginMethod({
methodArguments: [loginRequest],
userCallback: callback,
});
},
But I get an error and I can't figure out what is the thing that create this error :
Exception while invoking method 'login' Error: Match error: Unknown key in field username
...
Sanitized and reported to the client as: Match failed [400]
I founded some informations in the web but nothing that really helped me. I think it's generated when I call Accounts.callLoginMethod
My form looks like this:
<form>
<div class="row">
<div class="input-field col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
<label for="loginUsername">Username</label>
<input id="loginUsername" type="text" class="form-control" disabled="{{showLoading}}" required>
<br>
<label for="loginPassword">Password</label>
<input id="loginPassword" type="password" class="form-control" disabled="{{showLoading}}" required>
</div>
</div>
<br>
{{#if showLoading}}
{{> loading}}
{{else}}
<div class="text-center">
<button type="submit" class="btn btn-primary">Login</button>
</div>
{{/if}}
</form>
Someone could help me or know what is creating this error ?
Here is my 2 cents. Accounts.callLoginMethod is technically not a documented API function and in theory could change in any future Meteor release. Since it's not documented, the errors that it returns are not well defined and could be confusing.
Since you are just doing password authentication, I would recommend you use Meteor.loginWithPassword(user, password, [callback]) instead. At least this way you have a set of API documentation to fallback on if you get errors such as this (it also returns more specific errors when something goes wrong).
Try switching and see if you still receive an error output. If so the error will be one of the below error messages and you can better debug to see what's going on.
“Unrecognized options for login request [400]” if user or password is undefined.
“Match failed [400]” if user isn’t an Object or String, or password isn’t a String.
“User not found [403]” if the email or username provided in user doesn’t belong to a registered user.
“Incorrect password [403]” if the password provided is incorrect.
“User has no password set [403]” if user doesn’t have a password.
If you encounter one of the above errors, then do console.log(username) and make sure it is a string or object with the value that you are expecting.