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.
Related
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 have a base template base.html from which other templates inherit from.
After a user logs on I want to display their userID on navigation bar defined in base.html
So I have:
class CloseHandler(tornado.web.RequestHandler):
def get(self):
ui=db.users.find_one({"Username": ui0})
self.render("thanks.html" , ui1 = ui["Username"])
I also want to display the value of ui1 in the base template as: <p>{{ ui1 }}</p>
The only way round this is to move my navigation bar html code from base.html and insert it into every other template.
I however get this error:
NameError: global name 'ui1' is not defined
I've tried setting this as a global variable but it still gives the same error.
How could variables be used in different templates then?
I know this question is old, but I recommend creating a base class, that your handlers extend:
class BaseHandler(tornado.web.RequestHandler):. This will allow you to add functions available to all your handlers.
Next you can extend the tornado.web.RequestHandler.get_template_namespace() function, and add your global variables to the namespace:
# class BaseHandler
def get_template_namespace(self):
namespace = super(BaseHandler, self).get_template_namespace()
namespace.update({
'username': self.get_current_username() # or however you retrieve users
})
return namespace
Everything passed to the namespace is provided to self.render in all your handlers.
Now you can access {{ username }} in either base.html or the view extending it.
I have a django template with a context variable myVar, set in the view function.
This template also renders a custom simple template tag {% myTemplateTag %} that renders myTemplate.html
I want to use myVar inside the custom template tag that renders myTemplate.html.
Is there a way to inherit the context variables of my view function in the custom template tag? (without passing it explicitly as a parameter to the template tag)?
Using simple_tag
Using simple_tag, just set takes_context=True:
#register.simple_tag(takes_context=True)
def current_time(context, format_string):
timezone = context['timezone']
return your_get_current_time_method(timezone, format_string)
Using a custom template tag
Just use template.Variable.resolve(), ie.
foo = template.Variable('some_var').resolve(context)
See passing variables to the templatetag:
To use the Variable class, simply instantiate it with the name of the
variable to be resolved, and then call variable.resolve(context). So,
for example:
class FormatTimeNode(template.Node):
def __init__(self, date_to_be_formatted, format_string):
self.date_to_be_formatted = template.Variable(date_to_be_formatted)
self.format_string = format_string
def render(self, context):
try:
actual_date = self.date_to_be_formatted.resolve(context)
return actual_date.strftime(self.format_string)
except template.VariableDoesNotExist:
return ''
Variable resolution will throw a VariableDoesNotExist exception if it cannot resolve the string passed
to it in the current context of the page.
Might be useful too: setting a variable in the context.
Perhaps you could include the myTemplate.html file instead of rendering it with a special tag? Have you looked at the include tag? If you include myTemplate.html it will share the context with the one including it.
I have tried this a few ways. Per the docs, I have done this in my app's ini:
[app:myapp]
#...
jinja2.filters =
islist = myapp.machines.islist
My function is simply:
def islist(item):
return isinstance(item, list)
I can tell the filter setup line in the ini file is being read, because if I get the dotted path to my islist function wrong, the app throws an error.
However, when I try to use the islist function in a template, the template can't find the function.
{% if islist([]) %}a list{% else %}not a list{% endif %}
Results in this:
UndefinedError: 'islist' is undefined
What am I doing wrong?? Any help would be awesome.
I think filters are not exposed as functions in the namespace but rather are only invoked via the pipe. For example {{ foo | some_filter }}.
I'm trying to create a custom template tag that only renders a block of code once, regardless of how many times the tag/partial that contains it is executed.
This is how I've implemented it, but as you can see, it's a bit hackish:
my_partial.html:
{% once mycontent %}
this will only show once
{% endonce%}
my_template.html:
{% load my_tags %}
{% for i in list %}
{% my_partial %}
{% endfor %}
my_tags.py:
#register.inclusion_tag('my_partial.html',takes_context=True)
def my_partial(context):
return dict(arbitrary extra data)
#register.tag(name="once")
def do_once(parser, token):
try:
# Splitting by None == splitting by spaces.
tag_name, var_name = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError("%r tag requires arguments" % token.contents.split()[0])
nodelist = parser.parse(('endonce',))
parser.delete_first_token()
return DoOnceNode(nodelist, var_name)
class DoOnceNode(template.Node):
def __init__(self, nodelist, var_name):
self.nodelist = nodelist
self.var_name = '_do_once_'+var_name
def render(self, context):
request = context['request']
# Make request.GET mutable.
request.GET = dict(request.GET)
if self.var_name in request.GET:
return ''
else:
request.GET[self.var_name] = 1
return self.nodelist.render(context)
Specifically, I'm using the request.GET dictionary as a mutable global scope. It's hackish and obviously not what the request object is designed to do, but it works.
Ideally, I'd like to use something like the context, but I found that it isn't shared between calls to this tag. i.e. self.var_name in context is always False, rendering it useless as a global scope.
Why isn't context shared the same way request is shared? Is there someway to make it truly shared, or is there some other object I can use to store globally accessible variables within a request?
I'm not exactly sure what you need to accomplish or if your approach is indeed the best approach, but I'd suggest your look into the forloop.first variable before you go too far down this road. Your approach seems awkward at best at a glance, but I could be wrong since I don't know the specifics of the situation
django for template tag
Most likely you should be able to make this work to your needs, however if it falls short I'd suggest that the source for the for template tag (and it's forloop variable) would likely be very illustrative on how you might implement what you're looking to do.
What I ended up doing is saving a variable within the context and checking for that in the Node's render method:
class CustomNode(template.Node):
def render(self, context: dict) -> str:
context['already_rendered'] = context.get('already_rendered', set())
if self.__class__ in context['already_rendered']:
return ''
context['already_rendered'].add(self.__class__)
...