I'm using webapp with Google App Engine.
I recently added a call to request.get('variable_name'). This worked fine, but completely changed the contents of request.body.
Upon closer examination, it looks like if I do not make a call to request.get(), then request.body yields text without any url formatting. But after a call the request.get(), request.body now contains text that includes URL formatting (a lot of '%' signs, etc...).
Am I using webapp wrong? should I not be mixing and matching these two methods for information retrieval?
Here is some sample code:
class profiles_resource(webapp.RequestHandler):
def post(self):
# Value of request.body in debugger: 'str: {"query":"SELECT..."
token = self.request.get('token')
# Value of request.body in debugger: '%7B%22query%22%3A%22SELECT..."
request.get looks for request parameters in both the query string and the body of the request, assuming the body is formencoded. If you intend to read the body directly, do not use self.request.get or self.request.POST.
Related
Is it possible with Flask Restplus to create a model for a multipart/form-data request so that I can use it to validate the input with #api.expect?
I have this complex data structure for which I've created a api.namespace().model that has to be received together with a file. However when I tried to document the endpoint I noticed that this doesn't seem to be supported by Flask Restplus.
I've tried to find something along the lines of
parser = ns.parser()
parser.add_argument("jsonModel", type=Model, location="form")
parser.add_argument("file", type=FileStorage, location="files")
and
formModel = ns.model("myForm", {"jsonModel": fields.Nested(myModel), "file": fields.File})
But neither methods seem to support this kind of behavior.
I've got a Django app with Tastypie, and mainly BackBone client side. One of my models has a few ImageFields. Here is a similar setup to help me explain the issue.
settings.py
MEDIA_URL = "/media/"
models.py
class Foo(models.model):
bar = models.ImageField()
baz = models.CharField()
api.py
class FooResource(ModelResource):
class Meta:
queryset=models.Foo.objects.all()
resource_name = "foo"
authorization = Authorization()
When I make a GET request to the API, it appends the MEDIA_URL to the file names to return the URI where a bar can be accessed. However, when I change the value of baz on a row, and then make a PUT request with that, it also changes the value for a bar to the URI. This means that the next time I GET the row, it appends the MEDIA_URL again, breaking the system and appending it for each successive GET and PUT. I end up with values for bar in the DB that look like.
/media/media/media/bar.jpg
I think I should fix this by overriding a method in my ModelResource, so that when there is a PUT request, it recognizes that it's getting either a URI or a real file, and alters its behavior in some way.
Is this the correct fix? Could you provide some implementation details of a fix?
I found the answer. Tastypie is well designed, similarly to Django. Unfortunately I was not familiar with the terminology so when I read the docs I didn't understand. You can easily modify behavior of the API at many levels. Here is my new API definition, which fixed the issue.
api.py
class FooResource(ModelResource):
class Meta:
queryset=models.Foo.objects.all()
resource_name = "foo"
authorization = Authorization()
def hydrate_bar(bundle):
bundle["bar"] = bundle["bar"].strip(MEDIA_URL)
return bundle
I should add that this only works for me because I exclusively POST my image files individually with a post_detail method which doesn't call this method. If I was to POST or PUT image files as part of the entire row, I expect this might raise an error if that isn't considered.
In my coffeescript frontend I attempt to pass a list of value to the backend
data = {
id: [2, 3, 4]
}
$.post url, data
In the handler in a Google app engine (python) backend, I read the value like so:
id_value = self.request.get('id')
LOG.info("%s", id_value)
It always print out '2' only.
How can I get the backend to obtain the list [2,3,4]?
$.post by default sends data in url-encoded format, which handles nested structures in its own way.
You might need to encode the data in JSON before sending and then decode it on the server side - an example is here.
The request object provides a get() method that returns values for arguments parsed from the query and from POST data.
If the argument appears more than once in a request, by default get()
returns the first occurrence. To get all occurrences of an argument
that might appear more than once as a list (possibly empty), give
get() the argument allow_multiple=True.
Hence you should use something like the below snippet. You can find more details here.
id_value = self.request.get('id', allow_multiple=True)
If you need to access variables url encoded in the body of a request (generally a POST form submitted using the application/x-www-form-urlencoded media type), you should use something like this.
id_value = self.request.POST.getall('id')
I am trying to create a RESTish service using grails. I have the following...
def delete(Question q){
def text = request.reader.text;
def slurper = new JsonSlurper();
def result = slurper.parseText(text)
println "Request body is ${text} but the parsed version has a text of ${q.text} whereas the slurper gives me ${result as JSON}"
render noteService.delete(result.key)
}
This gives me an output of...
Request body is {"text":"Test Text","desc":"Test Desc","voteCount":0,"key":0} but the parsed version has a text of null whereas the slurper gives me {"desc":"Test Desc","key":0,"text":"Test Text","voteCount":0}
Why is this not wiring properly? The command object looks as follows...
#Validateable
class Question {
Integer key
String text
String desc
Integer voteCount
}
Is the delete request a GET under the hood or something? Is it expecting some other format?
Update
The create (POST) request is wiring fine which leads me to believe it is something with the differance between the Restangular call and what grails is expecting (So I think my request type guess might be right). My restangular code is simply...
this.delete = function(index) {
var questionToUpdate = _this.questions[index];
questionToUpdate.remove();
}
Also appears to fail with update (put) as well
Grails version is 2.4.3
Problem was that request.reader.text turns of the parsing of command objects.
I recently started using bottle and GAE blobstore and while I can upload the files to the blobstore I cannot seem to find a way to download them from the store.
I followed the examples from the documentation but was only successful on the uploading part. I cannot integrate the example in my app since I'm using a different framework from webapp/2.
How would I go about creating an upload handler and download handler so that I can get the key of the uploaded blob and store it in my data model and use it later in the download handler?
I tried using the BlobInfo.all() to create a query the blobstore but I'm not able to get the key name field value of the entity.
This is my first interaction with the blobstore so I wouldn't mind advice on a better approach to the problem.
For serving a blob I would recommend you to look at the source code of the BlobstoreDownloadHandler. It should be easy to port it to bottle, since there's nothing very specific about the framework.
Here is an example on how to use BlobInfo.all():
for info in blobstore.BlobInfo.all():
self.response.out.write('Name:%s Key: %s Size:%s Creation:%s ContentType:%s<br>' % (info.filename, info.key(), info.size, info.creation, info.content_type))
for downloads you only really need to generate a response that includes the header "X-AppEngine-BlobKey:[your blob_key]" along with everything else you need like a Content-Disposition header if desired. or if it's an image you should probably just use the high performance image serving api, generate a url and redirect to it.... done
for uploads, besides writing a handler for appengine to call once the upload is safely in blobstore (that's in the docs)
You need a way to find the blob info in the incoming request. I have no idea what the request looks like in bottle. The Blobstoreuploadhandler has a get_uploads method and there's really no reason it needs to be an instance method as far as I can tell. So here's an example generic implementation of it that expects a webob request. For bottle you would need to write something similar that is compatible with bottles request object.
def get_uploads(request, field_name=None):
"""Get uploads for this request.
Args:
field_name: Only select uploads that were sent as a specific field.
populate_post: Add the non blob fields to request.POST
Returns:
A list of BlobInfo records corresponding to each upload.
Empty list if there are no blob-info records for field_name.
stolen from the SDK since they only provide a way to get to this
crap through their crappy webapp framework
"""
if not getattr(request, "__uploads", None):
request.__uploads = {}
for key, value in request.params.items():
if isinstance(value, cgi.FieldStorage):
if 'blob-key' in value.type_options:
request.__uploads.setdefault(key, []).append(
blobstore.parse_blob_info(value))
if field_name:
try:
return list(request.__uploads[field_name])
except KeyError:
return []
else:
results = []
for uploads in request.__uploads.itervalues():
results += uploads
return results
For anyone looking for this answer in future, to do this you need bottle (d'oh!) and defnull's multipart module.
Since creating upload URLs is generally simple enough and as per GAE docs, I'll just cover the upload handler.
from bottle import request
from multipart import parse_options_header
from google.appengine.ext.blobstore import BlobInfo
def get_blob_info(field_name):
try:
field = request.files[field_name]
except KeyError:
# Maybe form isn't multipart or file wasn't uploaded, or some such error
return None
blob_data = parse_options_header(field.content_type)[1]
try:
return BlobInfo.get(blob_data['blob-key'])
except KeyError:
# Malformed request? Wrong field name?
return None
Sorry if there are any errors in the code, it's off the top of my head.