class MainHandler(BaseHandler.Handler):
def get(self):
user = users.get_current_user() or "unknown"
posts = memcache.get('posts_%s' % user.user_id())
if not posts:
q = db.Query(P.Post)
q.filter('author =', users.get_current_user()).order("-date")
posts=q.fetch(5)
memcache.set(key='posts_%s:'%user.user_id(),value=posts)
#q=P.Post.all().filter('user =',users.get_current_user())
self.render('index.html', user=user, posts=posts)
def post(self):
user = users.get_current_user() or "unknown"
author = users.get_current_user()
title = self.request.get('title')
content = self.request.get('content')
p = P.Post(author=author, title=title, content=content)
p.put()
res = memcache.get('posts_%s'%users.get_current_user().user_id())
if res:
res+=p
if len(res)>5:
res=res[1:]
else:
res=[p]
memcache.replace("posts_%s"%user.user_id(),value=res)
self.redirect('/')
When the browser redirects to '/' the last added item isn't in the list(it is added only after reloading). This happens only when I am on development server(on GAE it works OK),and I wonder if it can happen on GAE and what's the problem with this code
Any suggestions would be highly appreciated.
UPD:thx,I made keys the same,but the problem still remains
You're not hitting memcache at all here. You're using a different key format in the post and get methods: in get you use "posts_user" whereas in post you use "user:posts", so the key is never found and you fall through to the db query. And, of course, the query is not up to date because of eventual consistency, which is presumably the whole reason you're using memcache in the first place.
Fix your memcache keys and this should work.
maybe the item is not in memcache when you do replace. Why do you use replace in this case? Any reason not to use memcache.set? In the get function, there is still one place where the key is posts_%s: which is different than the others.
Related
I got a website, most like a blog, and when I submit a post it immediately put it in the db and update the cache too. It looks like that when the cache try to update itself, the db is not updated, and I got a cache out of date, with I got a front page without the last post, I tried putting time.sleep(1), and then it worked but I want to know if that have to be with that I'm not using a parent for my entities, and if that is how may I do that?
def cachFront(update=False):
key="top"
entradas=memcache.get(key)
if entradas is None or update:
logging.error("DB QUERY")
post= db.GqlQuery("select * from dbEntradas order by fecha_creacion desc limit 10")
entradas=list(post)
memcache.set(key, entradas)
return entradas
class MainHandler(Handler):
def get(self):
entradas= cachFront()
self.render("index.html", entradas=entradas)
class NewPostHandler(Handler):
def renderizar(self, error="", titulo="", post=""):
self.render("entradas.html", titulo=titulo, post=post, error=error)
def get(self):
self.render("entradas.html")
def post(self):
titulo= self.request.get("title")
topic= self.request.get("topic")
post= self.request.get("post")
if titulo and post and (topic!="Choose one.."):
entrada= post_db.dbEntradas(title=titulo, post=post, topic=topic)
entrada.put()
time.sleep(1)// if i commet this line when i redirect i do not get a cache update intead i got the old page
cachFront(True)
self.redirect('/')
Notice the change I made to cachFront and to the call to cachFront inside of your post method to manually prepend your entry in the case where eventual consistency hasn't replicated.
def cachFront(update=False, prepend=None):
key="top"
entradas=memcache.get(key)
if entradas is None or update:
logging.error("DB QUERY")
post= db.GqlQuery("select * from dbEntradas order by fecha_creacion desc limit 10")
entradas=list(post)
# manually prepend item to cache if it isn't available yet because
# of eventual consistency
if prepend and (not entradas or not entradas[0].key == prepend.key):
entradas.insert(0, prepend)
entradas = entradas[0:10]
memcache.set(key, entradas)
return entradas
class MainHandler(Handler):
def get(self):
entradas= cachFront()
self.render("index.html", entradas=entradas)
class NewPostHandler(Handler):
def renderizar(self, error="", titulo="", post=""):
self.render("entradas.html", titulo=titulo, post=post, error=error)
def get(self):
self.render("entradas.html")
def post(self):
titulo= self.request.get("title")
topic= self.request.get("topic")
post= self.request.get("post")
if titulo and post and (topic!="Choose one.."):
entrada= post_db.dbEntradas(title=titulo, post=post, topic=topic)
entrada.put()
cachFront(update=True, prepend=entrada)
self.redirect('/')
To answer your question specifically, yes, you could use parent / ancestor entities to solve your problem. If you expect to never make a write to your dbEntradas entities more than around 1/second, then this can be a fine solution.
Using parent entities tells datastore to keep all of those entities that share an ancestor relationship on the same server (basically to not replicate the data). Thus, you won't have to use time.sleep() to allow your entity to be written, as the following GQL call will be guaranteed to see the data from your earlier put.
You will need to choose one entity that will be the parent of all entities, and use that every time you make a new entrada:
post_db.dbEntradas(title=titulo, post=post, topic=topic, parent=post_parent)
In this case, I would suggest just making your first post the default parent, and grab its key. So your code would become:
def post(self):
titulo= self.request.get("title")
topic= self.request.get("topic")
post= self.request.get("post")
if titulo and post and (topic!="Choose one.."):
parent_entrada = post_db.dbEntradas.query(post_db.dbEntradas.id == [[first_post_id_here]]).get()
entrada= post_db.dbEntradas(title=titulo, post=post, topic=topic, parent = parent_entrada)
entrada.put()
cachFront(True)
self.redirect('/')
I'm passing a model entity's id in a url in on handler, and in another I simply need to look up the property value from that id.
class Pdf(db.Model):
name = db.StringProperty(required=True)
class InputHandler(Handler):
def get(self):
self.render('cert_form.html')
def post(self):
name = self.request.get('name')
if name:
a = Pdf(name=name)
a.put()
self.redirect('/key=%s' % a.key())
else:
error = "Please enter your full name."
self.render('cert_form.html')
class PDFHandler(Handler):
def get(self, id):
a = db.Key.from_path('Pdf', id)
self.response.write(a.name())
application = webapp2.WSGIApplication([
(r'/', InputHandler),
(r'/key=(.)+', PDFHandler),
], debug=True)
self.redirect('%s' % str(a.key().id())) (besides the total redundancy of %s and str -- lose that str call!-) redirects to a URL that's a pretty arbitrary string. How are you routing that, presumably to PDFHandler, so it can get its id?
A more sensible and common approach would be to redirect to, say,
'/pdfh?id=%s' % urllib2.urlencode(whatever)
or probably better
'/pdfh?k=%s' % a.key().urlsafe()
and use self.request.get in the handler to get the id (or probably better the url-safe form of the key, ready for a ndb.Key(urlsafe=... call to give you the actual key to use).
These approaches make routing obvious -- with your very peculiar approach, I need to see the routing to explain exactly why it's not working (the chance that such weird routing would work are not zero, but, very minimal:-). BTW, I'll need to see both the first layer of routing in your app.yaml, and the second layer in your Python code.
Or, you could just switch to a more sensible URL scheme, and be done in 23 seconds:-).
I have a function that gets an image from a form, and put's it into the database along with the username. So, here is my database:
class Imagedb(db.Model):
name = db.StringProperty(required = True)
image = db.BlobProperty()
And here is the code that writes to the database:
class Change_Profile_Image(MainHandler):
def get(self):
if self.user:
self.render('change_profile_image.html', username = self.user.name, firstname=self.user.first_name)
else:
self.render('change_profile_image.html')
def post(self):
imagedb = Imagedb(name = self.user.name)
imageupl = self.request.get("img")
imagedb.image = db.Blob(imageupl)
imagedb.put()
self.redirect('/profile')
Any who, it works awesome. Except for one thing. What i'm trying to accomplish is only storing ONE profile picture. What ends up happening is this:
Say I am the user admin. Ill upload a display pic, that pic shows in the profile. Ill upload another one, that one shows. Cool, except for the fact that I have 2 objects in my database that have the name = admin attribute. I would like to edit this...
def post(self):
imagedb = Imagedb(name = self.user.name)
imageupl = self.request.get("img")
imagedb.image = db.Blob(imageupl)
imagedb.put()
self.redirect('/profile')
so that I can post images to the database, but if one exists, it is overwritten. Could anyone help me with this please? I'm relatively new to python and app engine.
If something is unclear, please let me know.
You want to set the key of the Imagedb entity to "name". Essentially, you don't need the name field, but you'll instantiate it like
imagedb = Imagedb(key_name = self.user.name)
The key is a required field on all entities. By using your user name as the key it means every time you refere to a given key, it's the same entity.
Ok, first I want to state that this is Google App Engine via Python.
Any who,
These are my handlers / routing where the problem is occuring. Please read below for context and specifics:
class GetImage(MainHandler):
def get(self):
img = db.get(self.request.get("entity_id"))
self.response.out.write(img.image)
class Profile(MainHandler):
def get(self, profile_name):
current_user = str(self.user.name)
profile_name = current_user
if self.user:
key='ag5kZXZ-c3VpdGVnYW1lcnINCxIHSW1hZ2VkYhgxDA'
imgs = db.GqlQuery("select * from Imagedb WHERE name =:1", current_user)
for img in imgs:
key = img.key() # this is the key
self.render('profile.html', profile_name = self.user.name, current_user = self.user.name, profile_image = key ,username = self.user.name, email = self.user.email, first_name = self.user.first_name, last_name = self.user.last_name, country = self.user.country, prov_state = self.user.prov_state, city_town = self.user.city_town)
else:
self.redirect('/register')
class Change_Profile_Image(MainHandler):
def get(self):
if self.user:
self.render('change_profile_image.html', username = self.user.name, firstname=self.user.first_name, current_user = self.user.name)
else:
self.render('change_profile_image.html')
def post(self):
imagedb = Imagedb(name = self.user.name)
imageupl = images.resize(self.request.get("img"), 200, 200)
imagedb.image = db.Blob(imageupl)
imagedb.put()
self.redirect('/profile/'+self.user.name)
app = webapp2.WSGIApplication([('/', MainPage),
('/register', Register),
('/article', ArticlePage),
('/profile/([^/]+)', Profile),
('/login', Login),
('/logout', Logout),
('/welcome', Unit3Welcome),
('/games', Games),
('/forum', Forum),
('/media', Media),
('/rank', Rank),
('/review', Reviews),
('/events', Events),
('/alreadyloggedin', AlreadyLoggedIn),
('/change_profile_image', Change_Profile_Image),
('/img', GetImage)],
debug=True)
Alright so here is where stuff gets loopy. If I change the Profile class to take -- get(self) and remove my reg expression from the routing for the profile class, my images work perfectly. As soon as I route to unique profiles, i.e. pass profile_name into the Profile handler and map the URL to that profile, I lose all functionality of my GetImage handler. When I look at the source code, nothing has changed. The image is still being passed into the template as per usual.
Does anyone have any idea as to what is going on here? I would really appreciate it. Thank you very much in advance. Hopefully my knowledge will catch up to you guys and I'll be answering questions soon :p.
It's difficult to answer your question without seeing a (simplified) version of your template.
There are also a couple of weird elements in your code that make it hard to tell what's going on. It's hard to format this as a comment, so I'm putting it as an answer, just so you can at least see it.
In your get request in your profile handler, you try to get the current user before you check that the current user exists. You also just throw away the profile_name element completely when you assign profile_name to current_user, so you'll never get a profile image for anything but the current user on a profile page.
You take in profile_name here, but never use it:
def get(self, profile_name):
current_user = str(self.user.name)
profile_name = current_user
You loop over imgs but replace the key each time, which means that if you return more than 1 image you can't tell that this has occurred and you overwrite anything but the last image in the query. One thing you should do is add a check to see if imgs is even truthy, so you can tell if you got any results whatsoever, that might (though I can't imagine how) explain why your image handler is failing.
Finally, you might check your source to see which image url is actually being requested in the template.
How could we make the django form to not validate if we are editing, not adding a new record. The code as following :
class PageForm(forms.Form):
name = forms.CharField(max_length=100,widget=forms.TextInput(attrs={'class':'textInput'}))
description = forms.CharField(max_length=300, required=False,widget=forms.TextInput(attrs={'class':'textInput'}))
body = forms.CharField(widget=forms.Textarea)
template = forms.CharField(max_length=30,widget=forms.TextInput(attrs={'class':'textInput'}))
navbar = forms.BooleanField(required=False, widget=forms.Select(choices=(('True','True'),
('False', 'False'))))
publish = forms.BooleanField(widget=forms.Select(choices=(('Published','Publish Now'),
('Private','Private'),
('Draft','Draft'))))
def save(self, page=None, commit=True):
data = self.cleaned_data
if not page:
page = models.Page(key_name=data['name'].replace(' ','-'))
page.name = data['name']
page.description = data['description']
page.body = data['body']
page.template = data['template']
page.publish = data['publish']
if commit: page.put()
return page
# prevent the same page 's name
def clean_name(self):
name = self.cleaned_data['name']
query = models.Page.all(keys_only=True)
query.filter('name = ', name)
page = query.get()
if page:
raise forms.ValidationError('Page name "%s" was already used before' % name)
return name
The purpose of this name validation is to prevent the records with the same name. BUt i found that, it also validate on edit, so we couldn't edit records, since it will said 'records with same name already exist'.
Actually for editing, the page param on save function wont be none, but prev record instead, and wil be none on saving a new one. But how we read this param, on clean_name function so we can now whether it is editing or creating?
Thanks a lot!
in your clean method, you can use self.initial to know whether it is adding or editing. If it is editing, the self.initial will not be empty. But when it is adding, self.initial will be dictionary of what the previous value.
If you are editing form, then the form has some instance, and you can check if that exists.
If it does, then you are probably editing existing object.. right?
Example:
If you are editing object with form, you create form object much like this:
form = MyForm(instance = myobject)
Then in your form class methods you can check if form has saved instance in a way that it is described here:
Test if Django ModelForm has instance
in your clean_name function exclude the current object from queryset
query.filter('name = ', name).exclude(pk=self.pk)
or change the if condition to check that page and current object are not the same.
Sorry, I couldn't comment below your guys post, don't know why.
#sunn0 : I didn't use django models, coz deploy the app in appengine, so use appengine model instead.
#Zayatzz : May you show a little code how to do it? Since whether we are adding or editing, we always bound the form to request.POST before validation, so don't know how to differentiate.
#Ashok : I made a workaround based on your suggestion. Since previously I didn't pass the pk to form, but passing the prev object as param instead, so couldn't exclude by using pk. So, I change the code and put additional key as pk (if create, let key empty, but if edit fill key with pk) and just check in if condition, if key field not empty, then it means we are editing. Not sure if it is best practice, but it works anyway.
I can suggest to override form's init method
https://stackoverflow.com/a/70845558/15080117
because there is an argument instance.