Im trying to make a form where users can upload a file. And I have a form that takes the users arguments and posts them to the UploadHandler that is mapped to the url that is passed as parameter to the renderfunction. It works up to the point that it renders the form, but when submitting the file it redirects me to a blank page and the console shows me the error:
ERROR 2013-03-17 11:53:30,769 dev_appserver_blobstore.py:404] Could not find session for ahhkZXZ-a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA
INFO 2013-03-17 11:53:30,779 dev_appserver.py:3104] "POST /_ah/upload/ahhkZXZ- a3Vyc3NhbW1hbmZhdHRuaW5nYXJyGwsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxgxDA HTTP/1.1" 404 -
Im still new at this but I can't seem to find whats going on. I understand that the mappings somehow f***s up, but still I don't understand why it doesn't redirect me to the correct handler (SummaryHandler)?
The class that serves the form for file upload:
class CreateHandler(BaseHandler):
def get(self):
self.render('create.html', upload_url = blobstore.create_upload_url('/upload'))
The html-form ('create.html'):
<h2 class="main-title">Upload a file!</h2>
<form action="{{upload_url}}" method="post" enctype="multipart/form-data">
<label>
<div>Upload:</div>
<input type="file" name="file" accept="application/pdf"><br>
</label>
<div class="error">
{{file_error}}
</div>
<br>
<input type="submit" name="submit" value="Submit">
</form>
The uploadhandler that takes the form args:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file')
blob_info = upload_files[0]
#validation of form-vars
have_error=False
file_data=dict()
if not (blob_info.content_type == 'application/pdf'):
file_data['file_error']="You can only upload files in pdf-format."
have_error=True
if have_error:
self.render('create.html', **file_data)
else:
#'parent' is just some stuff for organising the database.
#Not important in this context
the_file = A_File(parent = summary_key(),
file_key=str(blob_info.key()))
the_file.put()
if the_file:
self.redirect('/summary/%s' % str(the_file.key().id()))
else:
_error="Could not find the file"
self.render('welcome.html', error=_error)
The application handlers and their mappings:
app = webapp2.WSGIApplication([
(r'/', WelcomeHandler),
(r'/create', CreateHandler),
(r'/upload', UploadHandler),
(r'/summary/(\d+)', SummaryHandler) #I have not included this handler in the text
], debug=True)
It might be the fact that this session expired or it was used. The urls that created using create_upload_url have an expiration (I think 10 min). Before actually posting you might want to refresh that URL using JavaScript.
Also if for any reason the file was uploaded in the Blobstore you won't be able to use the same URL again, it is only good for one request (could have multiple files though).
In your example, try to remove all the file specific checks, try to upload something and check through the administrative console in the Datastore Viewer if you have any blobs or session keys. This can be accessed through this URL:
http://localhost:8080/_ah/admin/datastore
Related
I am very very new to Python and Bottle and how server requests/responses work, and there are still quite a few basic and general ideas that I am trying to wrap my head around. That being said, there's some fundamental logic that I am missing out on.
In order to familiarize myself with bottle, python, html, etc. I have this very basic project. I have a form written in html (stored in tpl file) with some radios, checkboxes, select menus, and text input fields. I want to store all of those inputs and print them out to a new results page when the user clicks submit. As a sort of base trial to store all of the variables, I started with just the first name variable, and I wanted to print the stored variable to the url ('/fname'). If I can get this working then my plan is to change the post method to route to ('/fanpage/results') and then return all of the information on that page (possibly using another template, but I haven't gotten that far yet)
from bottle import route, run, post, get, request, template, static_file
HOST = 'localhost'
(...)
#post('/fname')
def show_fname():
fname = request.forms.get('fname')
return "<p>Your name is {{fname}}.</p>"
(...)
run(host=HOST, port=8080, debug=True)
(...)
<form action="/fanpage/results" class="needs-validated">
(...)
<div class="row">
<div class="col>
<label for="fname" class="form-label">First name:</label>
<input type="first name" class="form-control" id="fname" name="fname" placeholder="Enter first name" title="Please enter your first name" required>
<div class="invalid-feedback">Please fill out this field to continue.</div>
</div>
</div>
(...)
</form>
I have some route and get methods before the post method to send the tpl files to the server to display the form, which work. When I try to go to localhost:8080/fname, though, I get a 405 error - method not allowed. I feel like I am directly copying what I've seen in the documentation and online and I'm not sure why the url isn't working. If I try to change the route for the post method to the url ('/fanpage/results'), I get a 404 not found error.
Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?
Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?
No data is "pulled" from templates as such. The workflow is like this:
Client sends a request to a server's route. The request is typically a GET or POST request method, and the route is something like localhost:8080/fname. The server has to expose a matching request method and route.
The server takes action on the data sent with the request (if any) and sends a response. The response might be a simple status code (like a 405 Method Not Allowed) or it could have a body data payload along with it. The body could be an HTML page, CSS, JSON or something else. To build an HTML page, the server will want to inject dynamic data into a template. Other times, the HTML is purely static (unchanging).
When the response is received from the server, the client (which could be a web browser or a utility like curl or postman) shows the response to the user in whatever form it's designed to (web browsers render the HTML, for example, which often involves kicking off more requests to get relevant CSS files, scripts or images included in the HTML).
Now, let's say the response was HTML, possibly the result of rendering a template on the server. If that HTML has a form in it (or JS code that triggers a request asynchronously, avoiding a page refresh), the user can re-submit the form and kick off the process all over again.
One more thing: when you navigate your browser to, say https://en.wikipedia.org or localhost:8080/fname, that's a GET request, not a POST request, which explains the 405 response code since you told /fname to only respond to POST requests.
If you want to test a POST route, either use curl, postman or write a simple GET route that responds with a form that looks like <form action="/fname" method="post">.
Here's a minimal example of all of this wired together:
server.py
from bottle import post, request, route, run, template
#route("/")
def index():
return template("index.tpl")
#post("/fname")
def fname():
return template("fname.tpl", fname=request.forms.get("fname"))
if __name__ == "__main__":
run(host="localhost", port=8080, debug=True, reloader=True)
index.tpl
<!DOCTYPE html>
<html>
<body>
<form action="/fname" method="post">
<label for="fname">First name:</label>
<input name="fname" placeholder="Enter first name" required>
<input type="submit">
</form>
</body>
</html>
fname.tpl
<!DOCTYPE html>
<html>
<body>
<p>Your name is {{fname}}.</p>
<p>Back</p>
</body>
</html>
You can start a server, then navigate to http://localhost:8080 to fill out the form and see the result, or use a utility like curl on the command line to POST the fname:
PS C:\Users\me> curl -X POST -F 'fname=foo' http://localhost:8080/fname
<!DOCTYPE html>
<html>
<body>
<p>Your name is foo.</p>
<p>Back</p>
</body>
</html>
You can see the server responded with HTML, the result of injecting the fname variable from the POST payload into the fname.tpl template. A browser would render this as a webpage, but on the command line you just see the HTML code. In a JSON API, the response on the command line would be easier to manipulate, but here the server is designed for clients using browsers.
I am making a small project - Reminider System. I have a form which accepts values from users and inserts into the database table. The problem is occurring while fetching a value from a textbox. Below is my code and also I am giving what error am getting.
<form method="POST" action="">
<input type="hidden" name="unique" value="{{session.UID}}" disabled="true">
<button type="submit" class="btn btn-primary">Confirm</button>
</form>
This is my template
#app.route('/home/set_reminder',methods=['POST'])
#is_logged_in
def set_reminder():
if request.method=='POST' and form.validate():
uid = request.form['unique']
I am getting the error in this line uid = request.form['unique']. Not getting why it cannot fetch the value.
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'unique'
And this is the error which am getting.
Please help me out.
In your html, the uid input is disabled, so the browser will not send uid in a POST request's body. This causes the error when you try to access request.form.uid - it doesn't exist.
You could use readonly rather than disabled if the value must be returned by the browser.
See this answer for a little more information on BadRequestKeyError.
I have the following form shown in a modal of a bootstrap page
the aim is to upload a file with a hidden id
the application is based on Google App Engine, Python/Webapp2
<form action="someUrl" role="form" method="POST" enctype="multipart/form-data">
<input type="hidden" name="entityId" value="{{datastoreEntity.key.id()}}"/>
<input name="importFile" type="file" multiple>
<input type="text" class="form-control" readonly>
<input type="submit" name="submit" value="Import">
</form>
the problem is in the related RequestHandler (server side) where I can retrieve the file with
raw_file = self.request.POST.multi['importFile'].file
But I can't get the id (which is correctly generated by Jinja2 - checked in the page source). I have already tried with
self.request.get('entityId')
self.request.POST['entityId']
self.request.POST.multi['entityId']
I can't reproduce the problem as you relate it in the simplest of ways. I've copied that template into form.html, direct / to MainHandler, and net of the usual preparations (imports, jinja_environment with a FileLoader in the current dir, etc, I have):
class It(ndb.Model):
name = ndb.StringProperty()
class MainHandler(webapp2.RequestHandler):
def get(self):
dse = It(name="Willy")
dsek = dse.put()
datastoreEntity = dsek.get()
template = jinja_environment.get_template('form.html')
self.response.write(template.render(dict(
datastoreEntity=datastoreEntity,
)))
def post(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write(self.request.get('entityId'))
Visiting /, picking a file, and clicking the Import button, I see on my browser: 5066549580791808 -- which seems to be a typical datastore entity ID, as desired.
Please "interpolate" between this toy super-simplified version, and the no doubt much more complex things you're trying to do, and edit your Q to show us the very simplest way you can reproduce your problem, thanks!
Uploading via file inputs in App Engine requires that the app use a path that's obtained from the blobstore API. The doc for that is here. So you probably want to do something like
upload_url = blobstore.create_upload_url('/someUrl')
and then inject that into your template so that the form can use it
<form action="{{upload_url}}" ...
Once the file upload is complete, blobstore will redirect to /someUrl, where you process the rest of the form, including .file.
I have an HTML form that allows file uploads. I need to send the uploaded file via email. I am following the Google appengine Request class example.
from google.appengine.ext import webapp
from google.appengine.api import mail
class MyRequestHandler(webapp.RequestHandler):
def get(self):
self.response.out.write('''
<html>
<body>
<form method="post">
<p>File: <input type="file" name="upload" /></p>
<p><input type="submit" /></p>
</form>
</body>
</html>
''')
def post(self):
uploaded_file = self.request.get("upload")
uploaded_name = "file.doc"
message = mail.EmailMessage(sender="Me <me#mydomain.com>",
subject="Email from me")
message.to = "Me <me#mydomain.com>"
message.body = "See attached file."
message.attachments = [(uploaded_name, uploaded_file)]
message.send()
The above code works. I need to replace the hardcoded file name (file.doc) in the message.attachments tuple with the actual name of the file uploaded. I have two questions:
How do I get the uploaded file name from the webapp Request class?
Where can I find the documentation that describes how to do this? I have done a bunch of searching and can not find reference material or examples.
UPDATE:
Looks like the solution is:
if uploaded_file != "":
uploaded_name = self.request.params["upload"].filename
...as posted
here.
Instead of going self.request.get("upload"), try examining the self.request.POST["upload"] dict.
Specifically, self.request.POST['upload'] will return a cgi.FieldStorage instance. The attribute you are looking for is filename. Also of interest would be mimetype - and of course data.
For a complete how-to on handling uploads, take a look at this ancient post over at Nick Johnson's blog.
For an under-the-hood look at how webapp2 request data works, consult this portion of webapp2 docs.
I am trying to read and print a file in Google App Engine, but the code bellow seems unresponsive. I can upload the file, and my expectation was that it would just print the text, but it does nothing. I thought about adding a submit button, but I have no idea how to link submit with pythons printing. How can I get this to print on command?
I have seen the example provided by GAE here, but I would first like to keep it all on one page, and second I still don't understand how the submit calls that second page.
import webapp2
from google.appengine.ext.webapp import util
class MainPage(webapp2.RequestHandler):
#http://bukhantsov.org/2011/12/python-google-app-engine-calculator/
def get(self):
# build a list of operations
self.response.out.write("""<html>
<body>
<form action='/' method='get' autocomplete='off'>
<input type='file' name='file'/><br/>
#<input type='submit' name="test" value="submit">
</form>
</body>
</html>""")
file = self.request.get('file')
self.response.out.write(file)
app = webapp2.WSGIApplication([('/', MainPage)], debug=True)
def main():
util.run_wsgi_app(app)
if __name__ == '__main__':
main()
Your form is sent using the HTTP GET method, but for file uploads you need POST. Change it from:
method='get'
to:
method='post'
You will also need to handle POST requests in a different method. The POST body itself should be available as self.request.POST. So you end up with something like:
def post(self):
file = self.request.POST['file']
self.response.out.write(file)