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.
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('/')
Here is a url containing the hash for a super-secret feed:
http://127.0.0.1:8000/something/feed/12e8e59187c328fbe5c48452babf769c/
I am trying to capture and send the variable '12e8e59187c328fbe5c48452babf769c' which is feed_hash (acts as a slug to retrieve the particular entry)
Based on the example in django-syndication, I've created this simple class in feeds.py
class SomeFeed(Feed):
title = 'feed title '+request.feed_hash #just testing
link = "/feed/"
description = "Feed description"
def items(self):
return Item.objects.order_by('-published')[:5]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.content
# item_link is only needed if NewsItem has no get_absolute_url method.
def item_link(self, item):
return 'link'
Hence I am wondering, how would I modify this to get a model according to the hash?
At this time I cannot access the 12e8e59187c328fbe5c48452babf769c in any way. How might I access this and -- in a standard Django way -- create a feed from the retrieved variable (which represents a slug accessing a many-to-many relationship.)
First of all, set your parameter in django URL dispatcher. Something like:
url(r'^feed/(?P<pid>\w+)/$', SomeFeed())
Now retrieve and return the hash from the URL using the get_object method on your feed class. After all, get the hash as the second parameter of your method items().
class SomeFeed(Feed):
def get_object(self, request, pid):
# expect pid as your second parameter on method items()
return pid
# you can also load an instance here and get it the same way on items()
return SomeFeed.objects.get(pk=pid)
def items(self, feed):
# filter your feed here based on the pid or whatever you need..
return Item.objects.filter(feed=feed).order_by('-published')[:5]
I have yet to see anybody implement this pattern and am eager to learn if it's even technically viable. Let me provide an example of what the pattern would look like using a custom filter:
In this example, the "get_widget" filter will look for MyWidget objects with the name or key passed in as the first argument.
Template Logic
{% get_widget "whizbang" as item %}
<h1>{{item.name}}</h1>
{% get_widget "1234" as item %}
<h1>{{item.name}}</h1>
Custom Filter
#register.assignment_tag(takes_context=True)
def get_widget(context, widget_name):
try:
return MyWidget.objects.get(name=widget_name)
except MyWidget.DoesNotExist:
return None
But that seems rather hideous.
What I'd like to see is something a little more dyanmic:
Example:
Retrieve an instance of MyWidget based on its name being "whizbang" or, alternatively, using it's key.
In the template:
<h1>{{MyWidget.whizbang}}</h1>
<h1>{{MyWidget.1234}}</h1>
The question is twofold:
Would it be possible to pass in a singleton/factory to the
request_context
Is there a Python mechanism to "intercept" a method
call and interpret it before it's executed?
After sifting through the Python docs it looks like a combination of __getattrr__() and passing in a class name is all that was required. My apologies for answering my own question. Hopefully this will be useful for someone else.
Template
{{ Menus.HeyDude.DoSomething }}
Class
from mycode import Menu
class MenuFactory():
def __getattr__(self, name):
try:
return Menu.object.get(name=name)
except Menu.DoesNotExist:
raise AttributeError
Middlewear context processor
from mycode import MenuFactory
def context_processor(request):
return_val = {}
# Add all of the factories
return_val['Menus'] = MenuFactory
return return_val
I feel like I hacked this into tornado and it is in poor form. The goal was to get an error message down into a template. This error message would only need to be within one handler (responsible for that same page).
The template line:
{% if errormsg is not None %}
<div class="alert-warning">{{ errormsg }}</div>
{% end %}
The relevant handler section:
if auth:
self.set_current_user(username)
self.redirect(self.get_argument("next",u"/"))
else:
self.errormsg = "Login Failed"
self.render("login.html", errormsg=self.errormsg)
At this point I was getting global namespace error messages on the page when errormsg was not set to something.
NameError: global name 'errormsg' is not defined
The workaround I found was to muck around with the global render function within my BaseHandler (I do not like this one bit):
def render(self, template, **kwargs):
if hasattr(self, 'errormsg'):
kwargs['errormsg'] = self.errormsg
else:
kwargs['errormsg'] = None
super(BaseHandler, self).render(template, **kwargs)
This basically adds the errormsg to every render now. Is there a correct way to do this that doesn't mess with the global render function?
Thanks!
Edit:
Because what I'm actually trying to do is pass different/multiple, non-standard kwargs parameters into inherited handlers, I actually really think I was looking for a better way to test, in this case errormsg, within the template context.
{% if 'errormsg' in globals() %}
This still feels pretty hacked into place since this issue is the first time globals actually showed up at all while working with tornado.
I do like extending render for setting kwargs default values for all inherited handlers (what it is actually for). I think this may also be similar to how self.current_user works.
Overriding render() is officially supported, but it's a bit cleaner to override get_template_namespace instead: http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.get_template_namespace
Or, if errormsg is an attribute of the RequestHandler, you can just access handler.errormsg in the template - the handler variable is always set to the current RequestHandler.
My url has a keyword "shop_name" variable.
There's also the Shop model with "name" field.
In my ListView class I need to make repeating queries to Shop model to get a unicode variable from Shop.get_type() method. Depending on the result, a proper template directory is selected or queryset (Using subclassed django models).
Here's the code.
class OfferList(ListView):
def get_template_names(self):
shop = Shop.objects.get(name=self.kwargs['shop_name'])
return ["shop/%s/offer_list" % shop.get_type()]
def get_queryset(self):
shop = Shop.objects.get(name=self.kwargs['shop_name'])
Offer = shop.get_offers_model()
return Offer.objects.all()
def get_context_data(self, **kwargs):
# again getting shop instance here ...
shop = Shop.objects.get(name=self.kwargs['shop_name'])
context = super(OfferList, self).get_context_data(**kwargs)
context['shop'] = shop
return context
Question is what is the best way, so I can get some var (shop in this case) available for all methods ? I'm not a python guru (may be the basic problem). I've tried with init overriding but then I couldn't get exchange_name (specified in urls.py) to get the right "shop" instance. I would like to avoid repeating.
Thanks
Save it in self.shop.
get_queryset is the first method called (see the code for BaseListView's get method). So one solution would be to get your variable there, just as you do in your code, and then also save it to self.shop (just as the BaseListView does with self.object_list).
def get_queryset(self):
self.shop = Shop.objects.get(name=self.kwargs['shop_name'])
Offer = self.shop.get_offers_model()
return Offer.objects.all()
Then in your other methods you can use self.shop:
def get_template_names(self):
return ["shop/%s/offer_list" % self.shop.get_type()]