GAE upload image then dynamically serve the image - google-app-engine

I'm trying to make a simple app on GAE that allows a user to enter a url to an image and a name. The app then uploads this image to the Datastore along with its name.
After the upload the page self redirects and then should send the image back to the client and display it on their machine.
After running all I get is a Server error. Since I am new to GAE please could someone tell me if my code is at least correct.
I can't see what is wrong with my code. (I have checked for correct indentation and whitespace). Below is the code:
The python:
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp.RequestHandler):
def get(self):
name = self.request.get('image_name')
default_tile = get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile.image)
def get_default_tile(name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)
The HTML:
<html>
<head>
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>
<body>
<form action="/upload" method="post">
<div>
<p>Name: </p>
<input name="image_name">
</div>
<div>
<p>URL: </p>
<input name="image_url">
</div>
<div><input type="submit" value="Upload Image"></div>
</form>
<img src="default_tile_img?{{ image_name }}">
</body>
</html>
Any help at all will be so much appreciated. Thanks you!
UPDATE
Thanks to Greg, I know know how to view error logs. As Greg said I was missing a comma, I have updated the code above.
The app now runs, but when I upload an image, no image shows on return. I get the following message in the log:
File "/Users/jamiefearon/Desktop/Development/My Programs/GAE Fully functional website with css, javascript and images/mywebsite.py", line 53, in get
default_tile = self.get_default_tile(name)
TypeError: get_default_tile() takes exactly 1 argument (2 given)
I only passed one argument to get_default_tile() why does it complain that I passed two?

You're missing a comma after ('/upload', Upload) in the WSGIApplication setup.

use this python code
import jinja2 # html template libary
import os
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
import urllib
import urllib2
import webapp2
from google.appengine.ext import db
from google.appengine.api import urlfetch
class Default_tiles(db.Model):
name = db.StringProperty()
image = db.BlobProperty(default=None)
class MainPage(webapp2.RequestHandler):
def get(self):
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render())
class Upload(webapp2.RequestHandler):
def post(self):
# get information from form post upload
image_url = self.request.get('image_url')
image_name = self.request.get('image_name')
# create database entry for uploaded image
default_tile = Default_tiles()
default_tile.name = image_name
default_tile.image = db.Blob(urlfetch.Fetch(image_url).content)
default_tile.put()
self.redirect('/?' + urllib.urlencode({'image_name': image_name}))
class Get_default_tile(webapp2.RequestHandler):
def get_default_tile(self, name):
result = db.GqlQuery("SELECT * FROM Default_tiles WHERE name = :1 LIMIT 1", name).fetch(1)
if (len(result) > 0):
return result[0]
else:
return None
def get(self):
name = self.request.get('image_name')
default_tile = self.get_default_tile(name)
self.response.headers['Content-Type'] = "image/png"
self.response.out.write(default_tile)
app = webapp2.WSGIApplication([('/', MainPage),
('/upload', Upload),
('/default_tile_img', Get_default_tile)],
debug=True)

Related

Displaying Python database on Flask web server

I'm working on a project that requires that I create a create a database, import data into it, and display it on a FLASK local server. I've created the database, but I'm confused as to what I need to do in order to display its tables on the server. I have it set up to display information via HTML through render_template and believe I've established a connection via config.py, but I'm not sure where to go from here. I've read the following guides, but I don't quite understand them. If anybody could assist, I would appreciate it.
http://flask.pocoo.org/docs/0.12/patterns/sqlalchemy/
http://flask-sqlalchemy.pocoo.org/2.3/config/
Below are the relevant files in the project root.
/project
config.py
database_insert.py
server.py
/app
/templates
index.html
__init__.py
models.py
routes.py
config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SECRET_KEY = 'you-will-never-guess'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////project/app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
init.py
from flask import Flask, request, render_template
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
from app import routes, models
models.py
from app import db
class Lot(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(64), index=True)
spots = db.relationship('Spot', backref='author', lazy='dynamic')
def __repr__(self):
return '<Lot {}>'.format(self.username)
class Spot(db.Model):
id = db.Column(db.Integer, primary_key=True)
availability = db.Column(db.String(140))
spot_num = db.Column(db.String(140))
lot_id = db.Column(db.Integer, db.ForeignKey('lot.id'))
def __repr__(self):
return '<Spot {}>'.format(self.body)
routes.py
from app import app
from flask import Flask, request, render_template
#app.route('/')
#app.route('/index')
def index():
lot_details = {
'id': 'TEST_ID', #placeholder for testing purposes
'title': 'TEST_TITLE' #placeholder for testing purposes
}
return render_template('index.html', lot=lot_details)
index.html
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<p>Hello, World!</p>
<p>{{lot.id}}</p>
<p>{{lot.title}}</p>
</body>
At first, define helper method in your Spot model class.
class Spot(db.Model):
id = db.Column(db.Integer, primary_key=True)
availability = db.Column(db.String(140))
spot_num = db.Column(db.String(140))
lot_id = db.Column(db.Integer, db.ForeignKey('lot.id'))
def __repr__(self):
return '<Spot {}>'.format(self.body)
def dump(self):
return dict(id=self.id, title=self.title)
Then, in your flask route, you may query:
#app.route('/')
#app.route('/index')
def index():
lot_details = Lot.query.first().dump() # query first item
return render_template('index.html', lot=lot_details)

serve images using Google App Engine?

I am writing a simple application, that lets user upload images, and the application will show the image directly below the upload form. However I can't figure out how to show the image properly. I have tried several answers in stack overflow. None of them seem to answer the questions that I have. Could anyone please look at my code and see what I am doing wrong? Any help would be greatly appreciated.
import os
import urllib
import webapp2
from google.appengine.ext import db
import google.appengine.api.images
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
class MainHandler(webapp2.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>""")
#self.response.headers['Content-Type'] = 'image/png'
images = db.GqlQuery("select * from Images")
serving_url = ""
if images:
for x in images:
serving_url = google.appengine.api.images.get_serving_url(x.key(), 0, False, False)
self.response.out.write("""<div>Image:<img src="%s" </div> """ %serving_url)
else:
self.response.out.write("""<div>No Image Found</div>""")
self.response.out.write("""END</body></html>""")
class Images(db.Model):
image_db = db.BlobProperty()
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file') # 'file' is file upload field in the form
blob_info = upload_files[0]
self.redirect('/serve/%s' % blob_info.key())
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
resource = str(urllib.unquote(resource))
blob_info = blobstore.BlobInfo.get(resource)
self.send_blob(blob_info)
app = webapp2.WSGIApplication([('/', MainHandler),
('/upload', UploadHandler),
('/serve/([^/]+)?', ServeHandler)],
debug=True)
You're doing a few things wrong here.
Firstly, you are correctly using blobstore to store the images. To store reference a blobstore image in a model, you should be using db.BlobReferenceProperty.
Secondly, you're not actually storing that reference in your UploadHandler:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file') # 'file' is file upload field in the form
blob_info = upload_files[0]
instance = Images(image_db=blob_info)
instance.put()
self.redirect('/')
Finally, your GQL returns the Images instance, not of the blob. So your call to get_serving_url should be:
serving_url = images.get_serving_url(x.image_db)

I can not download a blob from GAE BlobStore

What is wrong with this code? I can not download a blob following the download url sent as response of the UploadHandler. I am getting a 404 response from the server.
I have doubts about how to send an url safe version of the blob key.
import urllib
import webapp2
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
MAIN = """<html>
<body>
<form action="%s" method="POST" enctype="multipart/form-data">
<p>Upload File:<input type="file" name="file"></p>
<p><input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
"""
DOWNLOAD = """<html><body><p>%s</p></body></html>"""
class MainHandler(webapp2.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/upload')
self.response.out.write(MAIN % upload_url)
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload_files = self.get_uploads('file') # 'file' is name field in the form
blob_info = upload_files[0]
_key = blob_info.key()
_url = '/download/%s' % str(_key)
_url_text = blob_info.filename
self.response.out.write(DOWNLOAD % (_url, _url_text))
class DownloadHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
blob_info = blobstore.Blob.get(resource)
self.sendblob(blob_info)
app = webapp2.WSGIApplication([('/', MainHandler),
('/upload', UploadHandler),
('/download/<resource>', DownloadHandler)],
debug=True)
app.yaml file is
application: georef
version: 1
runtime: python27
api_version: 1
threadsafe: false
libraries:
- name: webapp2
version: latest
handlers:
- url: /.*
script: georef.app
It looks like you copy and pasted the code wrong from the docs:
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
class ServeHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
resource = str(urllib.unquote(resource))
blob_info = blobstore.BlobInfo.get(resource)
self.send_blob(blob_info)
You're missing the line to unencode the resource string in case it has any strange characters in it.

Integrating Chat with (webapp2 + python 2.7 + Jinja2)

I'm trying to add chat to my site and am integrating some code with my existing code. The chat app works fine on its own when it's all set up in the original main.app file. But when I try to move that same code to a handlers.py file and then setup up routes in routes.py I get errors saying template variables are undefined. Are the two different codes conflicting in the way they render templates? They seem to be using webapp2 differently, i.e. my code renders templates like this:
self.render_template('secure_zone.html', **params)
And the chat app like this:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
Are both acceptable?
Here's my handlers.py file:
Routes are setup in routes.py and added in main.py
"""
import httpagentparser
from boilerplate import models
from boilerplate.lib.basehandler import BaseHandler
from boilerplate.lib.basehandler import user_required
class SecureRequestHandler(BaseHandler):
"""
Only accessible to users that are logged in
"""
#user_required
def get(self, **kwargs):
user_session = self.user
user_session_object = self.auth.store.get_session(self.request)
user_info = models.User.get_by_id(long( self.user_id ))
user_info_object = self.auth.store.user_model.get_by_auth_token(
user_session['user_id'], user_session['token'])
try:
params = {
"user_session" : user_session,
"user_session_object" : user_session_object,
"user_info" : user_info,
"user_info_object" : user_info_object,
"userinfo_logout-url" : self.auth_config['logout_url'],
}
return self.render_template('secure_zone.html', **params)
except (AttributeError, KeyError), e:
return "Secure zone error:" + " %s." % e
Here's the main.py file for the chat app:
import os
import hashlib
import urllib
import logging
import re
import json
import webapp2
import jinja2
from google.appengine.api import channel as channel_api # 'channel' is kind of ambiguous in context
from google.appengine.ext import db
from google.appengine.api import memcache
# This section will eventually get moved to a Handler class
template_dir = os.path.join(
os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(
loader = jinja2.FileSystemLoader(template_dir),
autoescape = True)
def render_str(template, **params):
'''Returns a string of the rendered template'''
t = jinja_env.get_template(template)
return t.render(params)
def render(template, **kw):
'''Render using the template and parameters'''
return(render_str(template, **kw))
# End Handler
class Main(webapp2.RequestHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
def post(self):
'''Displays chat UI'''
username = self.request.get('username')
channelname = self.request.get('channel')
usernameerror = ""
if not username:
usernameerror="Please enter a username"
elif not re.compile(r'^[a-zA-Z0-9_-]{3,20}$').match(username):
usernameerror = "Username must consist of 3-20 alphanumeric characters."
elif get_user(username):
usernameerror="Username already in use"
channelerror = ""
if channelname and not re.compile(r'^#[\w]{3,20}$').match(channelname):
channelerror="Channel must consist of 3-20 alpha_numeric characters and start with a #"
if len(usernameerror+channelerror) > 0:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
app = webapp2.WSGIApplication([
('/', Main),
('/communication', Communication),
('/_ah/channel/connected/?', Connect),
('/_ah/channel/disconnected/?', Disconnect)
], debug=True)
The specific error you posted in the comments "Error: 'Main' object has no attribute 'render_template'" is because in your Main handler, you try to return self.render_template. You should be just calling the function like this:
render_template("main.html", channel="#udacity")
Please note that I did not check the rest of your code, so if you run into any other issues, please post the specific errors you get.
It is because your webapp2.RequestHandler do not have the corresponding function "render_template"
you can use a BaseHandler with render_template function added to achieve the template rendering
from google.appengine.ext.webapp import template
class BaseHandler(webapp2.RequestHandler):
def render_template(self, filename, **template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
self.response.write(template.render(path, template_args))
class Main(BaseHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
ref: http://blog.notdot.net/2011/11/Migrating-to-Python-2-7-part-2-Webapp-and-templates
I just started using webapp2 + python 2.7 + Jinja2 few days, and that is the same problem I encountered.
Hope this solution can help you ;)

Google app engine error invalid string format

Hello guys i have a little problem i get an error:
"File "C:\Users\kokki\Desktop\gb1\main.py", line 36, in get
self.response.out.write(greeting.date.strftime('<b>posted: %d, %h %Y </b><br>'))
ValueError: Invalid format string"
can anyone help? heres the code:
import cgi
import datetime
import wsgiref.handlers
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.ext import webapp
class Greeting(db.Model):
author = db.UserProperty()
content = db.StringProperty(multiline=True)
date = db.DateTimeProperty(auto_now_add=True)
class MainPage(webapp.RequestHandler):
def get(self):
self.response.out.write('<html><body>')
greetings = db.GqlQuery("SELECT * FROM Greeting ORDER BY date DESC LIMIT 10")
for greeting in greetings:
self.response.out.write(greeting.date.strftime('<b>posted: %d, %h %Y </b><br>'))
if greeting.author:
self.response.out.write('<b>%s</b> wrote:' % greeting.author.nickname())
else:
self.response.out.write('An anonymous person wrote:')
self.response.out.write('<blockquote>%s</blockquote>' %
cgi.escape(greeting.content))
# Write the submission form and the footer of the page
self.response.out.write("""
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
</body>
</html>""")
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')
greeting.put()
self.redirect('/')
application = webapp.WSGIApplication([
('/', MainPage),
('/sign', Guestbook)
], debug=True)
def main():
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()
%h is an invalid placeholder for strftime. See the valid format specifiers at the time module docs (Python 2.5.2 docs).

Resources