I'm a newbie programmer and new to Google App Engine and webapp2 etc. So this may be a very basic question.
I am creating an application to store images into BlobStore. My model stores description, blob_key, image url and date.
I am able to save everything, so that bit is okay.
But now I want to create a delete button which will not only delete an item from the datastore, but also delete the image saved in the blobstore.
I have created a DeleteHandler, and in the html I have a form, passing the key for the item I want to delete. In the DeleteHandler, I am using the posted key to delete the item from the datastore. I am also trying to use the key to use it delete the image saved in the blobstore.
So far I'm getting a 404 on the delete form post, and even if I get past that, I'm not sure if my DeleteHandler is correct to handle the functionality I am looking for.
Any help would be much appreciated..
Main.py:
import os
import urllib
import webapp2
from google.appengine.ext.webapp import template
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.api import images
#Models
from google.appengine.ext import db
class ImageItem(db.Model):
description = db.StringProperty(required=True)
img_url = db.StringProperty()
blob_key = blobstore.BlobReferenceProperty()
when = db.DateTimeProperty(auto_now_add=True)
#Handlers (Views)
class MainHandler(webapp2.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/upload')
imgs = db.GqlQuery(
'SELECT * FROM ImageItem '
'ORDER BY when DESC')
imgs_dict = {'imgs': imgs}
self.response.out.write( template.render( 'main.html',locals() ) )
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
f = self.get_uploads('file')[0] # 'file' is file upload field in the form
img =ImageItem(description=self.request.get('description'))
img.blob_key = f.key()
img.img_url = images.get_serving_url( f.key() )
img.put()
self.redirect('/')
class DeleteHandler(webapp2.RequestHandler):
def post(self):
key = self.request.get('k')
item = db.get(key)
images.delete( item.blob_key )
item.delete()
self.response.out.write(key)
#URL Routing happens here
app = webapp2.WSGIApplication([('/', MainHandler),
('/upload', UploadHandler),
('/delete', DeleteHandler)],
debug=True)
Main.html:
<form action="{{upload_url}}" method="POST" enctype="multipart/form-data">
<p>
<label for="file">Upload File</label>
<input type="file" name="file" id="file">
</p>
<p>
<label for="description">Description</label>
<input type="text" id="description" name="description">
</p>
<input type="submit" name="submit" value="Submit">
</form>
<ul>
{% for i in imgs %}
<li>
<img src="{{i.img_url}}=s400-c" alt="">
{{i.description }}
</li>
<li>{{i.when }}</li>
<li>
<form action="/delete" method="POST" enctype="multipart/form-data">
<input type="text" name="k" value="{{i.key}}" />
<input type="submit" value="delete">
</form>
</li>
{% endfor %}
</ul>
The deletes are close. Once you have a key, you can delete entities by calling db.delete(key).
For your example, this would be something like this:
class DeleteHandler(webapp2.RequestHandler):
def post(self):
key = self.request.get('k')
item = db.get(key)
blobstore.delete([item.blob_key])
db.delete(item)
self.response.out.write(key)
Your url handling in main.py is good, so it's not obvious to me why you're getting a 404. You could double-check your app.yaml file to make sure all urls are passed to main.py.
Here's a sample app.yaml handlers url section:
handlers
- url: /.*
script: main.app
Got it to work. Thanks Eric, yours was real close. I needed to use blob_key.key().
Final code is following:
class DelHandler(webapp2.RequestHandler):
def post(self):
key = self.request.get('k')
item = db.get(key)
n = item.blob_key.key()
blobstore.delete(n)
item.delete()
self.redirect('/')
Related
I have a python file and an html file that interact with each other through the jinja2 environment in a manner similar to the one in this tutorial.
The following code controls the interaction between the html file and the python file:
class MainPage(webapp2.RequestHandler):
def get(self):
submission_query = Submission.query().order(-Submission.score)
submissions = submission_query.fetch(10)
template_values = {
'Submission' : Submission,
'submissions' : submissions,
}
template = JINJA_ENVIRONMENT.get_template('index.html')
self.response.write(template.render(template_values))
app = webapp2.WSGIApplication([
('/', MainPage),
('/create', CreateSubmmission),
('/voteup', VoteUp),
], debug=True)
I have a ndb model as follows:
class Submission(ndb.Model):
username = ndb.StringProperty()
placename = ndb.StringProperty()
link = ndb.StringProperty()
score = ndb.IntegerProperty()
I have an html form to create a new Submission as follows:
<form action="/create" method="post">
add a new location: <br>
your name:<div><textarea name="username" rows="2" cols="60"></textarea></div>
placename:<div><textarea name="placename" rows="2" cols="60"></textarea></div>
url:<div><textarea name="link" rows="2" cols="60"></textarea></div>
<div><input type="submit" value="Post"></div>
</form>
Using this request handler:
class CreateSubmmission(webapp2.RequestHandler):
def post(self):
submission = Submission()
submission.username = self.request.get('username')
submission.placename = self.request.get('placename')
submission.link = self.request.get('link')
submission.score = 0
submission.put()
self.redirect('/')
I have a section in my html which prints out each Submission along with a button for upvoting it:
{% for Submission in submissions %}
<p>
{{Submission.username}} posted:
<strong>{{Submission.placename}}</strong> <br>
score:{{Submission.score}}<br>
<!--Vote up button-->
<form action="/voteup?submission={{Submission}}" method="post">
<div><input type="submit" value="voteup"></div>
</form>
</p><br><br>
{% endfor %}
The upvoting is handled by the following python class:
class VoteUp(webapp2.RequestHandler):
def post(self):
submission = self.request.get('Submission')
submission_key = submission.put()
the_submission = submission_key.get()
the_submission.score +=1
the_submission.put()
self.redirect('/')
When pressing the button, the value of the respective Submission's score attribute should increase by one.
The code is implemented on the website sunlit-hook-91816.appspot.com. As can be seen on that site, pressing the upvote button generates the following error:
File "/base/data/home/apps/s~sunlit-hook-91816/1.383863233180213164/guestbook.py", line 52, in post
submission_key = submission.put()
AttributeError: 'str' object has no attribute 'put'
It appears that the VoteUp class is somehow unable to modify the value of Submission.score.
I am using the information found here but I can't figure out how to correctly apply it this problem. Can anyone tell me of a way to make the VoteUp class modify the Submission.score?
You're using the Submission object like it can be passed along between html/python code but it can't, you must add a reference in the form and dereference it back in you server.
Do this in the vote form:
<form action="/voteup?submission={{Submission.key.urlsafe()}}" method="post">
<div><input type="submit" value="voteup"></div>
</form>
We're getting the key of the submission in a format safe for http transport, so it can arrive back to the server unaltered.
Now when processing the vote, we can recreate the key and get the correct object:
class VoteUp(webapp2.RequestHandler):
def post(self):
submission = self.request.get('submission')
submission_key = ndb.Key(urlsafe=submission)
the_submission = submission_key.get()
the_submission.score +=1
the_submission.put()
self.redirect('/')
As you can see we're just recreating the key based on the formatted string we printed in the form.
Another way you could do this to make it even better is to use an extra input in the form:
<form action="/voteup" method="post">
<input name="submission" type="hidden" value="{{Submission.key.urlsafe()}}">
<div><input type="submit" value="voteup"></div>
</form>
Your code will work the same way, but I find it easier to read and maintain.
I have the following scenario:
<form class=*** method="post" action=("main.py" ???)>
<input class=*** type="email" name="email">
<input class=*** type="submit" value=***>
</form>
This form is in a .html file, evidently in a different file from the python code. I wish know which ways do I have to get the information from the form and send to the python file to finally work on it (I guess is something about the action field but not sure).
OBS: I must use the webapp2 (I'm using the google server so django and other stuff do not work on it)
You can see Handling Forms with webapp2 from the Google App Engine wepapp2 tutorial.
import cgi
from google.appengine.api import users
import webapp2
MAIN_PAGE_HTML = """\
<html>
<body>
<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 MainPage(webapp2.RequestHandler):
def get(self):
self.response.write(MAIN_PAGE_HTML)
class Guestbook(webapp2.RequestHandler):
def post(self):
self.response.write('<html><body>You wrote:<pre>')
self.response.write(cgi.escape(self.request.get('content')))
self.response.write('</pre></body></html>')
application = webapp2.WSGIApplication([
('/', MainPage),
('/sign', Guestbook),
], debug=True)
And read through the entire tutorial for more information about Datastore and template
Using template allow you to put code html code in another file.
I'm having trouble getting my ancestor queries to display the links associated with Tom's photos that are stored in the datastore. Nothing is displayed from the datastore even though there are several links in the datastore. Any assistance will be appreciated. Thanks in advance.
main.py
import webapp2
import jinja2
import os
from google.appengine.ext import db
jinja_environment = jinja2.Environment(
loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
class MainPage(webapp2.RequestHandler):
def get(self):
tom = Person(key_name='Tom')
wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://pinterest.com/pin/200691727117011230/'
wedding_photo.put()
baby_photo = Photo(parent=tom)
baby_photo.image_url='http://pinterest.com/pin/518828819542052681/'
baby_photo.put()
dance_photo = Photo(parent=tom)
dance_photo.image_url='http://pinterest.com/pin/257197828689352707/'
dance_photo.put()
dog_photo = Photo()
dog_photo.image_url='http://pinterest.com/pin/279575089339614779/'
dog_photo.put()
photo_query = Photo.all()
photo_query.ancestor(tom)
message = "Photos"
template_values = {
'message': message,
'photo_query': photo_query,
}
template = jinja_environment.get_template('index.html')
self.response.out.write(template.render(template_values))
class PhotoStore(webapp2.RequestHandler):
def post(self):
photo = Photo()
photo.image_url = self.request.get('image_url')
photo.put()
self.redirect('/')
class Person(db.Model):
name = db.StringProperty()
class Photo(db.Model):
image_url = db.StringProperty()
app = webapp2.WSGIApplication([('/', MainPage),
('/new_photo',PhotoStore)], debug=True)
index.html
<html>
<body>
<form action="/new_photo" method="post">
<label for="photo">Photo</label>
<div><textarea name="image_url" rows="1" cols="60" id="image_url"></textarea></div>
<input type="submit" value="Submit">
</form>
<hr></hr>
<div><b>{{ message }}</b></div>
<b>{% for p in photo_query.run(limit=5): %}</b>
<div>{{ p }}</div>
<b>{% endfor %}</b>
</body>
</html>
What do you see if you open the generated html source? I assume that the code {{ p }} just shows string representation of the Photo objects, which results in showing strings like <__main__.Photo object at 0xfcbd9ef0> that is interpreted as HTML tag and doesn't show in the rendered HTML.
Here is my code:
<form method="post" action="/del/" >
<input type="hidden" name="dealkey" value="{{deal.key()}}" />
<input class="btn btn-small" type="submit" value="Delete" />
</form>
class Delete(Handler):
def post(self):
dealid = self.request.get('dealkey')
dealid = int(dealid)
if dealid:
Book.delete(dealid)
self.redirect("/book")
app = webapp2.WSGIApplication([('/', MainPage),
('/book', BookMe),
('/del/',Delete),
],debug=True)
I am trying to use POST to delete entity in gae datastore, but i get 405, could anybody to explain it ?
How you define your Handler?
Or you can use the built-in handler and see if the error continue.
class Delete(webapp2.RequestHandler)
def post(self):
dealid = self.request.get('dealkey')
dealid = int(dealid)
if dealid:
Book.delete(dealid)
self.redirect("/book")
I was going through Appengine's Getting Started guide for python, while reading up templates,I modified the code(which is a simple guestbook) to include simple numbering next to the guestbook entries.
In index.html i added the (what I thought would be) relevant code.
index.html (I have added {%i=0%} on line 3,{%i+=1%} on line 5, and {{i}} on line 7, the rest is from the guide):
<html>
<body>
{%i=0%}
{% for greeting in greetings %}
{%i+=1%}
{% if greeting.author %}
<b>{{i}}:{{ greeting.author.nickname }}</b> wrote:
{% else %}
An anonymous person wrote:
{% endif %}
<blockquote>{{ greeting.content|escape }}</blockquote>
{% endfor %}
<form action="/sign" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>
{{ url_linktext }}
</body>
</html>
I got the following error
TemplateSyntaxError: Invalid block
tag: 'i=0'
I read up Django's help on templates, which says that
The Django template system provides
tags which function similarly to some
programming constructs – an if tag for
boolean tests, a for tag for looping,
etc. – but these are not simply
executed as the corresponding Python
code, and the template system will not
execute arbitrary Python expressions.
So I guess, that performing calculations is not what the templating system is for.
tl;dr
Can anyone tell me how to number my entries or use the "counter - increment" construct using Django templates?
I don't thing this code is necessary, but to complete the picture,
Unchanged helloworld.py code:
import os
from google.appengine.ext.webapp import template
import cgi
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db
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):
greetings_query = Greeting.all().order('-date')
greetings = greetings_query.fetch(10)
if users.get_current_user():
url = users.create_logout_url(self.request.uri)
url_linktext = 'Logout'
else:
url = users.create_login_url(self.request.uri)
url_linktext = 'Login'
template_values = {
'greetings': greetings,
'url': url,
'url_linktext': url_linktext,
}
path = os.path.join(os.path.dirname(__file__), 'index.html')
self.response.out.write(template.render(path, template_values))
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():
run_wsgi_app(application)
if __name__ == "__main__":
main()
From http://www.djangobook.com/en/beta/chapter04/
{% for item in todo_list %}
<p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}
It depends on what you need it for. If you need the actual count, you can use a for loop in the template: http://docs.djangoproject.com/en/dev/ref/templates/builtins/#for
Then you can use forloop.counter or forloop.counter0 to check the index.
If you just want to display a list of numbers you can use a html ordered list.
Django's template engine is for design only, and you want to keep calculations out of it. If you need to do something with your data at the template layer you can write a custom tag/filter.