How to properly query Datastore with Python? - issue with the basics - python

As a beginner in Python and App Engine I once again have to ask for your assistance. For some time I've been working with the book "Code in the Cloud" to learn the very basics of using App Engine. The thing is I have not been very successful so far. While most of the issues I have had stemmed from UTF-8 encoding/decoding, this time I have problems with making queries to Datastore.
To learn the basics I recreated the code for a simple chat service available in chapter IV of the aforesaid book. The key elements are:
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import datetime
from google.appengine.ext import db
#START: ChatMessage
class ChatMessage(db.Model):
user = db.StringProperty(required=True)
timestamp = db.DateTimeProperty(auto_now_add=True)
message = db.TextProperty(required=True)
def __str__(self):
return "%s (%s): %s" % (self.user, self.timestamp, self.message)
#END: ChatMessage
Above, I defined the datamodel for a message
class ChatRoomPage(webapp.RequestHandler):
def get(self):
self.response.headers["Content-Type"] = "text/html"
self.response.out.write("""
<html>
<head>
<title>AppEngine Chat Room</title>
</head>
<body>
<h1>Welcome to AppEngine Chat Room</h1>
<p>(Current time is %s)</p>
""" % (datetime.datetime.now()))
messages = db.GqlQuery("SELECT * From ChatMessage ORDER BY time")
for msg in messages:
self.response.out.write("<p>%s</p>" % msg)
self.response.out.write("""
<form action="/talk" method="post">
<div><b>Name:</b>
<textarea name="name" rows="1" cols="20"></textarea></div>
<p><b>Message</b></p>
<div><textarea name="message" rows="5" cols="60"></textarea></div>
<div><input type="submit" value="Send ChatMessage"/></div>
</form>
</body>
</html>
""")
# END: MainPage
In the block above I create UI and performe a GQL query for data I posted.
Lastly:
class ChatRoomPoster(webapp.RequestHandler):
def post(self):
chatter = self.request.get("name")
msgtext = self.request.get("message")
msg = ChatMessage(user=chatter, message=msgtext)
msg.put()
self.redirect('/')
I create the handler to send the collected data into Datastore.
Having consulted the book line by line, as well as Google tutorial, and a number of threads on this website, I still can't figure out, why the code does not work properly. Basically, the chat won't display past messages. My guess is I am doing something wrong with accessing the stored data, as the program does not throw any exception - I am missing an identifier or something like that. Perhaps I am again missing something very obvious.
I would really appreciate your help. Thank you in advance.

Your query is:
messages = db.GqlQuery("SELECT * From ChatMessage ORDER BY time")
but your datastore model doesn't have a time property, it has a timestamp property. Attempting to order by a property that isn't on any entities will always return an empty resultset.

In your for loop over the messages, 'msg' is a datastore object. You need to reference a specific attribute (ie msg.message).
for msg in messages:
self.response.out.write("<p>%s</p>" % msg.message)

Related

Flask + SqlAlchemy delete database entries

I have to delete database entries conditionally and I couldn't figure out as the form is asking for correct parameters posted in uri. I can delete entries in shell with same code but not in view. Following is the view and form:
#app.route('/cancelannualsub/<int:student_id>', methods=['DELETE'])
def cancel_yearly_rec(student_id):
if not user_authorized():
return redirect('/')
user = get_profile_data(session['auth_token'])
profile_data = user['StudentProfile']
pkg = Package.query.filter_by(student_id=profile_data.id).first_or_404()
if request.method=='POST':
try:
pkg = Package()
dbase.session.delete(pkg)
flash('Package deleted successfully.')
dbase.session.commit()
except:
pass
return redirect('plans')
return render_template('profile/cancel/cancel.html')
Form:
<form action="{{ url_for('cancel_yearly_rec', student_id=***can't render value here***) }}" method='post'>
<input type='hidden' name='_method' value='DELETE'
<input class='btn' type="submit" value="Cancel" />
</form>
I am trying different things from stackoverflow examples. Please assist if I am doing something wrong or if there is a better way to do it.
I wasn't using proper formatting to render model objects inside try block.
It supposed to be something like:
mymodel = Model.query.filter_by(variable=some_field_data).first_or_404()
dbase.session.delete(mymodel)
dbase.session.commit()
For readers don't get confused with "dbase" because I made this import like:
from app import db as dbase
You can import it simply by:
from app import db
or add "as anything".
Then you reference anything in rest of your file where you've made import.

Tornado: pattern for Flask's flashed messages

In Flask, flashing messages on redirect is done using the flash function in the view + {% for message in get_flashed_messages() %} in the template. Tornado doesn't seem to have anything like that built in (which is fine with me, fwiw).
Only replacement I've seen so far looks like this (part of this gist):
class AuthLoginHandler(BaseHandler):
def get(self):
errormessage = self.get_argument("error", default="")
self.render("login.html", errormessage = errormessage)
def post(self):
...(code)...
if not auth:
error_msg = u"?error=" + tornado.escape.url_escape("Login incorrect")
self.redirect(u"/auth/login/" + error_msg)
And then in the template:
<span class="errormessage">{{errormessage}}</span>
Is there a cleaner pattern?
(I can see how one could do multiple messages with this pattern, and a couple ways of cleaning it up, but that's not what I'm asking.)
Flask's flash by default uses cookies, so a direct translation would be to use self.set_secure_cookie("flash", message) to set a message and self.get_secure_cookie("flash"); self.clear_cookie("flash") to read it back.

Google App Engine: HTTP Error 403: Forbidden

I'm using Google Books API to integrate with an E-commerce site that I'm developing. The idea is to let the users search for books using a search bar, and then calling the Google API to output the number of books corresponding to the search keyword.
However, I get a 403 Forbidden error after I click submit after entering my query in the form. This is strange, because this never happened when I was testing my application on the localhost. Here's the code for my application:
main.py
class SearchHandler(Handler):
def get(self):
self.render("search.html")
def post(self):
keey = self.request.get('keey')
finaal = "https://www.googleapis.com/books/v1/volumes?q=" + keey + "&key=MY_APP_KEY"
f = urllib2.urlopen(finaal).read()
self.render("jsony.html", finaal = finaal)
app = webapp2.WSGIApplication(('/search', SearchHandler)], debug=True)
search.html
<html>
<head>
<title>Web Mining</title>
</head>
<body>
<form method = "post">
Book Name:<input type = "text" name = "keey">
<input type = "submit">
</form>
</body>
</html>
jsony.html
<html>
<head>
<title>Web Mining</title>
</head>
<body>
<form method = "post">
{{finaal}}
</form>
</body>
Now, the jsony.html is still incomplete. All I'm doing now is displaying the URL which contains the outputted json in it's raw, unprocessed form.
What seems to be causing this 403 error to arise after I deploy my application ?
EDIT 1:
The problem resolves when I remove the following line from my main python file:
f = urllib2.urlopen(finaal).read()
However, I would be needing my API's URL in order to extract data from its source code. What's happening ?
Try adding &country=US to the URL query string.

Updating vote counts in Google App Engine

I am trying to create a voting button for posts using Google App Engine.
Currently I am implementing it like this:
class Latest(Handler):
def get(self):
posts = recent_posts()
qt = memcache.get('recent_posts_qt')
if qt:
qt = time.time() - qt
self.render('latest.html', articles = posts, qt = qt)
def post(self):
post_id = int(self.request.get("id"))
q = AllPost.get_by_id(post_id)
q.votes += 1
q.put()
time.sleep(0.5)
update = recent_posts(update=True) # for memcache
posts = db.GqlQuery("SELECT * FROM AllPost ORDER BY created DESC LIMIT 10")
posts = list(posts)
self.render('latest.html', articles=posts)
The html I am using is this:
<div class="article-votes">Votes: {{item.votes}}
<form action="/latest" method="post">
<input name="id" value={{item.key().id()}}>
<button>Vote+</button>
</form>
</div>
If I try to refresh the page after voting on a post, I get the "confirm form submission alert". There must be a better way to implement this without this happening. Is it possible to update the vote count and datastore without rendering the page again?
Refreshing a POST request will always trigger that confirmation window, it is a common behavior in most browsers. You can control the form POST request closely via Javascript using AJAX (XMLHTTPRequest) and that wouldn't cause another page render.

Upload files in Google App Engine

I am planning to create a web app that allows users to downgrade their visual studio project files. However, It seems Google App Engine accepts files uploading and flat file storing on the Google Server through db.TextProperty and db.BlobProperty.
I'll be glad anyone can provide code sample (both the client and the server side) on how this can be done.
In fact, this question is answered in the App Egnine documentation. See an example on Uploading User Images.
HTML code, inside <form></form>:
<input type="file" name="img"/>
Python code:
class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
if users.get_current_user():
greeting.author = users.get_current_user()
greeting.content = self.request.get("content")
avatar = self.request.get("img")
greeting.avatar = db.Blob(avatar)
greeting.put()
self.redirect('/')
Here is a complete, working file. I pulled the original from the Google site and modified it to make it slightly more real world.
A few things to notice:
This code uses the BlobStore API
The purpose of this line in the
ServeHandler class is to "fix" the
key so that it gets rid of any name
mangling that may have occurred in
the browser (I didn't observe any in
Chrome)
blob_key = str(urllib.unquote(blob_key))
The "save_as" clause at the end of this is important. It will make sure that the file name does not get mangled when it is sent to your browser. Get rid of it to observe what happens.
self.send_blob(blobstore.BlobInfo.get(blob_key), save_as=True)
Good Luck!
import os
import urllib
from google.appengine.ext import blobstore
from google.appengine.ext import webapp
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
class MainHandler(webapp.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/upload')
self.response.out.write('<html><body>')
self.response.out.write('<form action="%s" method="POST" enctype="multipart/form-data">' % upload_url)
self.response.out.write("""Upload File: <input type="file" name="file"><br> <input type="submit" name="submit" value="Submit"> </form></body></html>""")
for b in blobstore.BlobInfo.all():
self.response.out.write('<li>' + str(b.filename) + '')
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file')
blob_info = upload_files[0]
self.redirect('/')
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, blob_key):
blob_key = str(urllib.unquote(blob_key))
if not blobstore.get(blob_key):
self.error(404)
else:
self.send_blob(blobstore.BlobInfo.get(blob_key), save_as=True)
def main():
application = webapp.WSGIApplication(
[('/', MainHandler),
('/upload', UploadHandler),
('/serve/([^/]+)?', ServeHandler),
], debug=True)
run_wsgi_app(application)
if __name__ == '__main__':
main()
There is a thread in Google Groups about it:
Uploading Files
With a lot of useful code, that discussion helped me very much in uploading files.
Google has released a service for storing large files. Have a look at blobstore API documentation. If your files are > 1MB, you should use it.
I try it today, It works as following:
my sdk version is 1.3.x
html page:
<form enctype="multipart/form-data" action="/upload" method="post" >
<input type="file" name="myfile" />
<input type="submit" />
</form>
Server Code:
file_contents = self.request.POST.get('myfile').file.read()
If your still having a problem, check you are using enctype in the form tag
No:
<form encoding="multipart/form-data" action="/upload">
Yes:
<form enctype="multipart/form-data" action="/upload">
You can not store files as there is not a traditional file system. You can only store them in their own DataStore (in a field defined as a BlobProperty)
There is an example in the previous link:
class MyModel(db.Model):
blob = db.BlobProperty()
obj = MyModel()
obj.blob = db.Blob( file_contents )
Personally I found the tutorial described here useful when using the Java run time with GAE. For some reason, when I tried to upload a file using
<form action="/testservelet" method="get" enctype="multipart/form-data">
<div>
Myfile:<input type="file" name="file" size="50"/>
</div>
<div>
<input type="submit" value="Upload file">
</div>
</form>
I found that my HttpServlet class for some reason wouldn't accept the form with the 'enctype' attribute. Removing it works, however, this means I can't upload any files.
There's no flat file storing in Google App Engine. Everything has to go in to the Datastore which is a bit like a relational database but not quite.
You could store the files as TextProperty or BlobProperty attributes.
There is a 1MB limit on DataStore entries which may or may not be a problem.
I have observed some strange behavior when uploading files on App Engine. When you submit the following form:
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="img" />
...
</form>
And then you extract the img from the request like this:
img_contents = self.request.get('img')
The img_contents variable is a str() in Google Chrome, but it's unicode in Firefox. And as you now, the db.Blob() constructor takes a string and will throw an error if you pass in a unicode string.
Does anyone know how this can be fixed?
Also, what I find absolutely strange is that when I copy and paste the Guestbook application (with avatars), it works perfectly. I do everything exactly the same way in my code, but it just won't work. I'm very close to pulling my hair out.
There is a way of using flat file system( Atleast in usage perspective)
There is this Google App Engine Virtual FileSystem project. that is implemented with the help of datastore and memcache APIs to emulate an ordinary filesystem. Using this library you can use in you project a similar filesystem access(read and write).

Categories

Resources