I would like to somehow instrument a mako.lookup.TemplateLookup such that it applies certain preprocessors only for certain file extensions.
Specifically, I have a haml.preprocessor that I would like to apply to all templates whose file name ends with .haml.
Thanks!
You should be able to customize TemplateLookup to get the behavior you want.
customlookup.py
from mako.lookup import TemplateLookup
import haml
class Lookup(TemplateLookup):
def get_template(self, uri):
if uri.rsplit('.')[1] == 'haml':
# change preprocessor used for this template
default = self.template_args['preprocessor']
self.template_args['preprocessor'] = haml.preprocessor
template = super(Lookup, self).get_template(uri)
# change it back
self.template_args['preprocessor'] = default
else:
template = super(Lookup, self).get_template(uri)
return template
lookup = Lookup(['.'])
print lookup.get_template('index.haml').render()
index.haml
<%inherit file="base.html"/>
<%block name="content">
%h1 Hello
</%block>
base.html
<html>
<body>
<%block name="content"/>
</body>
</html>
Related
I'm trying to render the text from bs4 Tags using Django Template Language.
For some reason bs4.Tag.text isn't accessible from template. I can render it's text only if I get it before and pass itself to template instead of Tag object.
Here is code that I used:
from django.template import Template, Context
from bs4 import BeautifulSoup
html_string = '<p>Some text.</p>'
soup = BeautifulSoup(html_string)
tag = soup.find('p')
template = Template('{{ tag.text }} - {{ tag_text }}')
context = Context({'tag': tag, 'tag_text': tag.text})
print(template.render(context))
Expected output:
Some text. - Some text.
Actual output:
- Some text.
I feel confused a bit. For my purposes it's necessary to pass raw bs4.Tag objects to extract text and other attributes after. Hope you can help me
You can try writing a custom templatetag for this.
app/templatetags/bstags.py
from django import template
register = template.Library()
#register.filter
def get_attr(obj, attr):
return getattr(obj, attr)
and then in templates call it as:
{% load bstags %}
{{ tag|get_attr:text }}
I have a script which takes uploaded data, munges it together, turns it into a plot (using Bokeh) and then exports it to a directory as JSON.
At some point in the future, a user can hit the right URL and the appropriate plot should be displayed to the user as part of the HTML template.
I can generate the plot. I can save it as JSON. I can get the URL to retrieve it as JSON, but I cannot get the JSON plot to render within the template.
I've had a dig around the Bokeh documentation and examples, but they all seem to use a flask app to serve the pages.
I think I'm on the right track, using views.py to find and return JSON as part of a render() response, and then have Bokeh.embed.embed_items() do the work in the template to make it look right, but it's not working out - everything but the plot is displayed.
1) Create the plot and puts it in the directory for later use (app/results/1)
create plot.py
import os
import json
from django.conf import settings
from bokeh.embed import json_item
from bokeh.plotting import figure
x=[1,2,3,4,5]
y=[0,-1,-2,3,4]
p=figure(title="test_example")
p.line(x, y)
#json_export = json_item(p, "result")
json_export = json_item(p)
with open(os.path.join(settings.RESULTS_DIR,"1", "test.json"), 'w') as fp:
fp.write(json.dumps(json_export))
2) Set up the url
urls.py
urlpatterns=[
path('result/<int:pk>', views.resultdetailview, name='result-detail'),
]
3) Take the request, use the pk to find the plot json and render it all in the appropriate template.
views.py
def resultdetailview(request, pk):
results=str(pk)
with open(os.path.join(settings.RESULTS_DIR, results, "test.json"), 'r') as fp:
#data=json.load(fp)
data=json.loads(fp.read())
#context={'result':data}
#return render(request, 'app/sandbox_detail.html', context)
return render(request=request,
context={'json_object':data, 'resources':CDN.render()})
NB: If I instead use return JsonResponse(data, safe=False) then the url returns the json successfully ...
I think therefore that the issue is in the template.
4) Show the wonderous plot to the user
sandbox_detail.html
<header>
<link rel="stylesheet" href="http://cdn.bokeh.org./bokeh/release/bokeh-0.11.1.min.css" type="text/css" >
<script type="text/javascript" src="http://cdn.bokeh.org./bokeh/release/bokeh-0.11.1.min.js"> </script>
</header>
<h1> Title: Test </h1>
<div>Test</div>
<body>
<div id="result"></div>
<script>
Bokeh.embed.embed_item({{json_object}}, "result");
</script>
</body>
This template renders everything but the 'result' div.
What have I missed?
This is what I see so far:
FIRST: You are mixing 2 methods for injecting plot json data into the page.
According to documentation you can do it using either of these two methods:
1) specify the div directly:
Python: json_data = json.dumps(json_item(p, "myplot"))
JavaScript: Bokeh.embed.embed_item(item);
2) specify the div in embed_item function:
Python: json_data = json.dumps(json_item(p))
JavaScript: Bokeh.embed.embed_item(item, "myplot");
But not both of them at the same time. Could this be the problem?
SECOND: Preferably don't insert Bokeh resources by hand: rather use CDN.render() or INLINE.render() to automatically include all that your script needs:
import json
from bokeh.resources import CDN
return render(request = request,
template_name = 'app/sandbox_detail.html',
context = { json_object = json.loads(json_string),
resources = CDN.render() } )
sandbox_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{{ resources }}
</head>
<body>
<div id="result"></div>
<script>
Bokeh.embed.embed_item({{ json_object }}, "result");
</script>
</body>
</html>
THIRD: Make sure what you embed in the page is json object not a string (see variable naming above)
It helps when you debug your rendered template in your browser's debug tool.
I tried a very similar approach and found one large flaw: My browser noted that it could not find the None object. The reason here is that python stores the empty value as None, while JavaScript expects a null object.
The solution? Python already translates None to null, when you run json.dumps. To keep it that way, read the json string as a string. So instead of your data=json.loads(fp.read()) use data=fp.read().
I want to call a python function from a jinja template. At the doucmentation (http://jinja.pocoo.org/docs/2.10/templates/ see Assignments) the following example is offered:
{% set key, value = call_something() %}
But the following source does not work.
import jinja2
#jinja2.contextfunction
def hw():
return "World"
template = jinja2.Template('{% set test = hw() %} Hello {{ test }}!')
template.render(name='John Doe')
Error: ... UndefinedError: 'hw' is undefined
Does any one knows how to solve the problem?!
Thank's to all!
You may simply supply it as a key=value pair in the template.render function, just as you have supplied the name variable.
import jinja2
def hw():
return "World"
template = jinja2.Template('{% set test = hw() %} Hello {{ test }}!')
print(template.render(name='John Doe', hw=hw))
Alternatively, if and when you plan to use a Jinja environment, you may add it to the globals dictionary before any templates are created, should you want the function to be accessible from every template within said environment.
import jinja2
def hw():
return "World"
template = """
{% set test = hw() %} Hello {{ test }}!
"""
env = jinja2.Environment()
env.globals['hw'] = hw
template = env.from_string(template)
print(template.render(name='John Doe'))
I've used the from_string method here to load your template, but there is a whole loaders system for environments which you may want to investigate.
All my JavaScript is run through Django's compiler, allowing me to inject HTML strings Underscore templates in the following manner:
var htmlStr = '{% filter convert_js_template %}{% include "my_file.html" %}{% endfilter %}'
This code runs the output from the included HTML file through the convert_js_template filter, which simply removes line breaks and escapes single quotation marks so that the final JS string is valid. To make it more readable, however, I'd like to be able to simply write something like the following:
var htmlStr = '{% convert_js_template "my_file.html" %}'
How can I create a convert_js_template function that will accomplish this? My sense is that it needs to start out by doing the following:
Grab the contents of the desired file
Parse the contents for any Django template tags
I tried the following:
#register.filter('convert_js_template')
def convert_js_template(path):
value = include(path)
return value.replace('\n', '').replace('\r', '').replace("'", "\\'")
I initially received the error NameError: global name 'include' is not defined, so then I added from django.conf.urls import include to the file and am now receiving a different error: ImportError: Import by filename is not supported.
And this is where I'm stuck :)
My solution doesn't involve a custom "include" function exactly. Instead, it manually loads and renders the desired template file:
from django.template import Library, loader, Context
...
register = Library()
...
#register.filter('js_template')
def js_template(template_path):
tpl = loader.get_template(template_path)
return tpl.render(Context({})).replace('\n', '').replace('\r', '').replace("'", "\\'")
js_template.is_safe = True
Note that template_path should be relative to the directory you have set for Django to look in for templates.
Usage:
var htmlString = '{{ "path/to/my_template.html" | js_template }}';
I'm adding an autocomplete feature to my django application.
HTML
<script type="text/javascript">
var a = {% get_fields_task_name %}
$( "#auto_task_name" ).autocomplete({
source: a
});
view
def get_fields_task_name():
task = Task.objects.all()
output = []
for e in task:
output.append(e.name)
x = json.dumps(output)
return x
get_fields_task_name = register.simple_tag(get_fields_task_name)
I need to pass parameters to get_fields_task_name, how can I do this in the template instead of {% get_fields_task_name %}?
This depends on exactly what the parameters are, but if it's something you can get in a single variable at template rendering time, just add it as a parameter to the function (see the docs for simple_tag).
For example:
#register.simple_tag
def get_fields_task_name(kind):
tasks = Task.objects.filter(kind=kind)
return json.dumps([e.name for e in tasks])
(using a list comprehension to make the function body shorter and cleaner, and also using the nicer decorator syntax).
Then you can just change the relevant line in the template to
var a = {% get_fields_task_name some_variable.kind %};