I'm trying to output in bibtex format in Django and the template looks like this:
#{{ pubentry.type }{,
author = {{% for author in pubentry.authors.all %}{{ author.first_name }} {{ author.middle_name }} {{ author.last_name }}{% if not forloop.last %} and {% endif %}
{% endfor %}},
title = {{{ pubentry.title }}},
journal = {{{ pubentry.journal }}}
}
The problem is with the {{{ or {{%. One way to go around the problem is to add a space after the first {, but that kind of tamper the format. What's the right way to escape { in Django templates?
Have a look at the templatetag tag:
Output one of the syntax characters used to compose template tags.
Since the template system has no concept of "escaping", to display one of the bits used in template tags, you must use the {% templatetag %} tag.
What you are after is:
{% templatetag openvariable %}
Maybe there is a nicer solution because this does not increase readability...
Another (more flexible) approach may be to convert the values to a bibtex-like value before sending them to your template. You'll probably need to do this anyway to escape some of the characters that bibtex/latex can't handle. Here's something similar I prepared earlier:
import datetime
class BibTeXString(unicode):
pass
def bibtex_repr(obj):
""" A version of the string repr method, that always outputs variables suitable for BibTeX. """
# If this has already been processed, it's ok
if isinstance(obj, BibTeXString):
return obj
# Translate strings
if isinstance(obj, basestring):
value = unicode(obj).translate(CHAR_ESCAPES).strip()
return BibTeXString('{%s}' % value)
# Dates
elif isinstance(obj, datetime.date):
return BibTeXString('{%02d-%02d-%02d}' % (obj.year, obj.month, obj.day))
# Integers
if isinstance(obj, (int, long)):
return BibTeXString(str(obj))
else:
return BibTeXString(repr(obj))
CHAR_ESCAPES = {
ord(u'$'): u'\\$',
ord(u'&'): u'\\&',
ord(u'%'): u'\\%',
ord(u'#'): u'\\#',
ord(u'_'): u'\\_',
ord(u'\u2018'): u'`',
ord(u'\u2019'): u"'",
ord(u'\u201c'): u"``",
ord(u'\u201d'): u"''" ,
ord(u'\u2014'): u'---',
ord(u'\u2013'): u'--',
}
You could even use this as a template filter if you wanted, making your template look like:
#{{ pubentry.type }{,
author = {% filter bibtex %}{% for author in pubentry.authors.all %}{{ author.first_name }} {{ author.middle_name }} {{ author.last_name }}{% if not forloop.last %} and {% endif %}{% endfor %}}{% endfilter %},
title = {{ pubentry.title|bibtex }},
journal = {{ pubentry.journal|bibtex }}
}
But I would escape the content before it gets to the template, so that your template just needs to do this:
#{{ pubentry.type }{,
{% for field in fields %}{{ field }}{% if not forloop.last %},{% endif %}{% endfor %}
}
Or even leave out the template altogether at this stage. Good luck!
With the templatetag template tag.
title = {% templatetag openvariable %}{% templatetag openbrace %} pubentry.title {% templatetag closevariable %}{% templatetag closebrace %},
Related
I'm trying to allow for dynamic template tags. Specifically, I have a menu setup that I'm defining in code vs templates. And I would like to render the menu label as {{ request.user }}. So how can I define that as a string in Python, and allow the template to parse and render the string as intended. And not just variables too, templatetags as well ({% provider_login_url 'google' next=next %}).
What am I missing?
Update with code:
I'm specifically designing my menus with django-navutils, but that's less important (basically the package just stores the defined data and then uses templates to render it).
from navutils import menu
top_horizontal_nav = menu.Menu('top_nav')
left_vertical_nav = menu.Menu('left_nav')
menu.register(top_horizontal_nav)
menu.register(left_vertical_nav)
sign_in = menu.AnonymousNode(
id='sign_in',
label='Sign In',
url='{% provider_login_url "google" next=next %}',
template='nav_menu/signin_node.html',
)
user = menu.AuthenticatedNode(
id='user_menu',
label='{{ request.user }}',
url='#',
template='nav_menu/username_node.html'
)
top_horizontal_nav.register(sign_in)
top_horizontal_nav.register(user)
What I would like to do, is now render these string values ('{{ request.user }}') in my templates
{% load navutils_tags %}
<li
class="{% block node_class %}nav-item menu-item{% if node.css_class %} {{ node.css_class }}{% endif %}{% if is_current %} {{ menu_config.CURRENT_MENU_ITEM_CLASS }}{% endif %}{% if has_current %} {{ menu_config.CURRENT_MENU_ITEM_PARENT_CLASS }}{% endif %}{% if viewable_children %} has-children has-dropdown{% endif %}{% endblock %}"
{% for attr, value in node.attrs.items %} {{ attr }}="{{ value }}"{% endfor %}>
<a href="{{ node.get_url }}" class="nav-link"{% for attr, value in node.link_attrs.items %} {{ attr }}="{{ value }}"{% endfor %}>{% block node_label %}{{ node.label }}{% endblock %}</a>
{% if viewable_children %}
<ul class="{% block submenu_class %}sub-menu dropdown{% endblock %}">
{% for children_node in viewable_children %}
{% render_node node=children_node current_depth=current_depth|add:'1' %}
{% endfor %}
</ul>
{% endif %}
</li>
So, for the above, where I'm rendering {{ node.label }}, how can I get the value stored in node.label to actually be parsed as a request.user? This similarly applies for the URL of value {% provider_login_url "google" next=next %}.
You can create a custom template tag and render those. Like this
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def render_nested(context, template_text):
# create template from text
tpl = template.Template(template_text)
return tpl.render(context)
Then in template
...
<a href="{{ node.get_url }}" class="nav-link"
{% for attr, value in node.link_attrs.items %} {{ attr }}="{{ value }}"{% endfor %}>
{% block node_label %}{% render_nested node.label %}{% endblock %}
</a>
...
Haven't tested but I think it will work.
More on template: https://docs.djangoproject.com/en/dev/ref/templates/api/#rendering-a-context
More on custom tags: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-tags
If I understand you well, you want to write template code that renders to template code and then be processed again. Something like ... meta-templates?
I can see you already figured out the first part, but now you need the resulting code to be parsed again in order to set the respective value for {{ request.user }}.
I achieve that by using middleware, but since parsing a template is an expensive operation I implemented some a mechanism to apply the "re-parsing" process just to urls/views of my selection.
The model.
class ReparsingTarget(models.Model):
url_name = models.CharField(max_length=255)
Simple enough, just a model for storing those URL names I want to be affected by the middleware.
The middleware.
class ReparsingMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
# We are no interested in whatever happens before
# self.get_response is called.
response = self.get_response(request)
# Now we will re-parse response just if the requested url
# is marked as target for re-parse.
if self._marked(request) and isinstance(response, TemplateResponse):
# Decode the template response code ...
content = response.content.decode('utf-8')
# Create a Template object ...
template = Template(content)
# Provide request to de context ...
context_data = response.context_data
context_data.update({'request': request})
# ... and renders the template back the the response.
response.content = template.render(Context(response.context_data))
return response
def _marked(self, request):
url_name = resolve(request.path_info).url_name
return ReparsingTarget.objects.filter(url_name=url_name).exists()
It is a really simple implementation, the tricky part for me was to figure out how to put the idea into Django code.
In practice.
Some model.
class Foo(models.Model):
label = models.CharField(max_length=255)
The template:
{% for foo in foos %}
{% comment %} Remember foo.label == '{{ request.user }}' {% endcomment %}
<p> Username: {{ foo.label }} </p>
{% endfor %}
The stored Foo instance:
And the result:
right now I have my models like this...
class myModel(models.Model):
title = models.CharField()
x_foo1_y = models.CharField()
x_foo2_y = models.CharField()
views...
somevar = myModel.objects.get(title='title')
if x:
x = 'foo1'
else:
x = 'foo2'
template...
{% if x == 'foo1' %}
{{ somevar.x_foo1_y }}
{% elif x == 'foo2' %}
{{ somevar.x_foo2_y }}
{% endif %}
what I want to do is add some logic to my view that defines foo1 or foo2 under certain circumstances and then my template could be cut down to be a lot easier to read...
{{ somevar.x_{{ x }}_y }}
But that doesn't work and I can't think of a way to make it work. Any ideas?
To call an arbitrary method of any object create a simple custom template tag:
from django import template
register = template.Library()
#register.simple_tag
def call_method(obj, method_name, *args):
method = getattr(obj, method_name)
return method(*args)
And then call it in the template:
{% load my_tags %}
{# equivalent of `somevar.x_foo1_y()` #}
{% call_method somevar 'x_foo1_y' %}
{# equivalent of `somevar.x_foo1_y('test')` #}
{% call_method somevar 'x_foo1_y' 'test' %}
{# equivalent of `somevar.x_foo1_y(othervar)` #}
{% call_method somevar 'x_foo1_y' othervar %}
{# equivalent of `somevar.x_foo1_y()` if `method_name == 'x_foo1_y'` #}
{% call_method somevar method_name %}
And, at last, equivalent of your {% if %} expression:
{# make the `x_{{ x }}_y` method name #}
{% with 'x_'|add:x|add:"_y" as method_name %}
{% call_method somebar method_name %}
{% endwith %}
I'm not sure what would x_foo1_y and x_foo2_y be, but I'm guessing you are generalising and asking how to handle situations like this. As such, I would suggest you use custom template tags, which might help in reducing your template logic by delegating some of it. Template tags can do pretty much anything, so I believe you could move lots of logic like this.
I have the following code, where I get all problem notes.
{% for n in task.task_notes.all %}
{% if n.is_problem %}
<li>{{ n }}</li>
{% endif %}
{% endfor %}
How would I only get the first problem note? Is there a way to do that in the template?
In the view:
context["problem_tasks"] = Task.objects.filter(is_problem=True)
# render template with the context
In the template:
{{ problem_tasks|first }}
first template filter reference.
Would be even better, if you dont need the other problem tasks at all (from 2nd to last):
context["first_problem_task"] = Task.objects.filter(is_problem=True)[0]
# render template with the context
Template:
{{ first_problem_task }}
Assuming you need all of the tasks in the template somewhere else.
You can make a reusable custom filter (take a look at first filter implementation btw):
#register.filter(is_safe=False)
def first_problem(value):
return next(x for x in value if x.is_problem)
Then, use it in the template this way:
{% with task.task_notes.all|first_problem as problem %}
<li>{{ problem }}</li>
{% endwith %}
Hope that helps.
use this code in the loop:
{% if forloop.counter == 1 %}{{ n }}{% endif %}
I noticed that my django code calls my database very often with the exact same queries.
I understand that a db hit is made when I actually need the data to display on a page or to evaluate. However, my template code looks like this:
template:
{% if item.listing %}
{{ item.name }} text <strong>{{ item.listing|lowestprice }}</strong> more text
{% else %}
{{ item.name }} even more text
{% endif %}
....
{% for listed_item in item.listing %}
....
{% endfor %}
custom filter:
def lowestprice(value):
try:
val = unicode(value[0].price) + unicode(value[0].symbol)
return val
except:
return "not available"
This code hits my db three times. First on template {% if .. %} second on my custom filter, third on the {% for %} loop.
listing is a method of my models class which is returning a raw SQL queryset with some very expensive joins.
def listing(self):
return Universe.objects.raw("ONE HELL OF A QUERY")
How can I reduce my code to hit the db only once?
Edit: Using with works, but is it possible to avoid db hits on custom filters?
You should use with to do the expensive query once and store it the context.
{% with item.listing as item_listing %}
{% if item_listing %} ... {% endif %} ... etc ...
{% endwith %}
I am wondering if we could use django filter as a variable where a python method formatting text can set the filter value which can be rendered by the template
The usual way filter works is like this
{{ html_text | safe }} or {{ plain_text | linebreaks }}
I want to set the values for filters 'safe'/'linebreaks' in a variable and render it like this
{{ text | filter_variable }}
Researched online, but couldn't figure it out, any help would be appreciated.
I second what Chris Pratt said, but I'd do it like this:
from django.template import defaultfilters
#register.filter
def apply_filter(value, filter):
return getattr(defaultfilters, filter)(value)
that way it works for any filter.
You can't do that, but you can create a custom filter that accepts a parameter:
from django.template import defaultfilters
#register.filter
def apply_filter(value, filter):
if filter == 'safe':
return defaultfilters.safe(value)
if filter == 'linebreaks':
return defaultfilters.linebreaks(value)
else:
# You might want to raise an exception here, but for example purposes
# I'll just return the value unmodified
return value
Then, in your template:
{{ text|apply_filter:filter_variable }}
This works if you have a few filters:
{% ifequal filter_variable 1 %}{{ text|safe }}{% endifequal %}
{% ifequal filter_variable 2 %}{{ text|linebreaks }}{% endifequal %}
{% ifequal filter_variable 3 %}{{ text|yourfilter }}{% endifequal %}