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('/')
Related
I'm fair new to Django and I am trying to log visitor for my blog. I'm using generic view for my blog and here is part of code:
#blog/urls.py
urlpatterns = patterns('',
#index
url(r'^(?P<page>\d+)?/?$', PostListView.as_view(
model=Post,
paginate_by=3,
)),
#individual post
url(r'^(?P<pub_date__year>\d{4})/(?P<pub_date__month>\d{1,2})/(?P<slug>[a-zA-Z0-9-]+)/?$',
DetailView.as_view(model=Post,)),
#cat
url(r'^category/(?P<slug>[a-zA-Z0-9]+)/?$', CategoryListView.as_view(
paginate_by=3,
model=Category,
)),
#tag
url(r'^tag/(?P<slug>[a-zA-Z0-9]+)/?$', TagListView.as_view(
paginate_by=3,
model=Tag,
)),
and I wrote a simple model for visitor log:
#tasks/models.py
class Visitor(models.Model):
visit_stamp = models.DateTimeField(auto_now_add=True)
referer = models.CharField(max_length=100, blank=True)
ip = models.IPAddressField(blank=True)
user_agent = models.CharField(max_length=100, blank=True)
page = models.CharField(max_length=100)
and its view:
#tasks/views.py
def log(request, page):
try:
hit = Visitor()
hit.page = page
hit.ip = request.META.get('REMOTE ADDR', '')
hit.last_visit = datetime.now()
hit.referer = request.META.get('HTTP REFERER', '')
hit.user_agent = request.META.get('HTTP_USER_AGENT', '')
hit.save()
except IntegrityError:
pass
def tracking(request, page):
log(request, page)
return render_to_response(page)
My question is how and where can I call this methods so that I can log a user is visiting a specific page. I'd appreciate any advices.
First off, I assume you don't have access to the apache (or whatever host is running your django app) logs and/or you want to eventually add other things and/or you want it available in the database, as otherwise, you can skip a lot of work and just grep the logs.
Anyways, I'd recommend rewriting track to work as a decorator (and adjust log as you need it... note that I believe you can get the URL from the request object versus passing it in as a page value in case you want to know which specific instance was visited). There are also ways you could probably do this with middleware, but this gives you a pretty good mix of simplicity and ability to control which views get logged.
To borrow an example from http://www.djangofoo.com/253/writing-django-decorators
def track(page):
def decorator(func):
def inner_decorator(request, *args, **kwargs):
log(request, page)
return func(request, *args, **kwargs)
return wraps(func)(inner_decorator)
return decorator
And then in your urls (or you can also do #track to decorate function based views)
url(r'^(?P<page>\d+)?/?$', track("index")(PostListView.as_view(
model=Post,
paginate_by=3,
))),
url(r'^someregexp$', track("pagename")(SomeListView.as_view(
model=Post,
paginate_by=3,
))),
Edit: meant to add. Do note that in general, GET requests are supposed to be idempotent; logging is a gray area but the main thing to keep in mind is that some requests may not get logged as you might expect if the page is cached (for Posts this shouldn't be an issue)
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.
I am new to Tornado, and I have this simplified code for the purposes of this question:
class LoginHandler(BaseHandler):
def get(self):
error_message = None
title = "Log in to your account"
self.render("login.html", error_message=error_message, title=title)
def post(self):
#function and params excluded for brevity of question
error_message = self.authenticate_user()
title = "Log in to your account"
self.render("login.html", error_message=error_message, title=title)
The self.render("login.html", error_message=error_message, title = title) as well as the title variable are repeated (seemingly unnecessarily) because otherwise, I get the error "Global variable 'title' or 'error_message' not defined," depending on whether I use post or get to render the page.
I have a different title for every page, and I was wondering how I can simply have one title variable and one self.render("login.html"...) per page handler (i.e., LoginHandler) that will work when either the get or post function is called. I don't like the verbatim repetition, but I am having trouble avoiding error messages when I don't do the same thing in both functions.
How can I solve this? Thank you.
You can avoid redeclaring the title and error_message variables by initiating them as class members. (I used the leading underscore _ in the variable name to indicate that this value should be private and is only to be used in this class.)
class LoginHandler(BaseHandler):
def __init__(self):
# Call the BaseHandler's __init__ function to initialize parent's members
BaseHandler.__init__()
self._title = "Log in to your account"
def get(self):
self.render("login.html", error_message=None, title=self._title)
def post(self):
self.render("login.html", error_message=self.authenticate_user(), title=self._title)
The added advantages of doing it this way is that you only need to change the title in one spot and you don't run the risk of getting a different title depending on whether the method was get or post.
NOTE: It appears that in error_message is not necessary - it's only being used in a single case. The self.render() calls do not receive the same parameters and therefore are both necessary.
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.
I have a Link and a Bookmark model like this:
class Link(models.Model):
url = models.URLField(unique=True)
def __unicode__(self):
return self.url
class Bookmark(models.Model):
title=models.CharField(max_length=200)
user=models.ForeignKey(User)
link=models.ForeignKey(Link)
def __unicode__(self):
return u'%s, %s' % (self.user.username, self.link.url)
Now within a view I see if a Link with a given url already exists.
This object is then passed next with the username to Bookmarks collection to see if a bookmark already exists with this username and Link instance already exists.
def bookmark_save_page(request):
if request.method == 'POST':
form = BookmarkSaveForm(request.POST)
if form.is_valid():
# Create or get Link
link, dummy = Link.objects.get_or_create(url=form.cleaned_data['url'])
# Create or get bookmark
bookmark, created = Bookmark.objects.get_or_create(user=request.user, link=link)
# Save bookmark to database
bookmark.save()
return HttpResponseRedirect('/user/%s/' % request.user.username)
This is the bit I don't understand. How does it know how to take the url field inside Link model as a way of comparison? Is it because I had defined it in the Link model like this?
def __unicode__(self):
return self.url
I am coming from .NET and there you have to define the GetHash() for the class as a way to specify how the instances should be compared against each other.
How does Python know this?
Thanks
I think you are asking "how does Django compare instances when filtering", rather than "how does python compare objects".
With the following line of code,
bookmark, created = Bookmark.objects.get_or_create(user=request.user, link=link)
Django is filtering on link object's primary key. The __unicode__ method does not matter.
See the Django docs for comparing objects and queries over related objects for more info.