I'm trying to understand the Version One - Use Namespace.attr example for accessing descendant attributes in Mako. I have the base page template in page.html, and the index page in index.html which inherits page.html. I want to allow page.html (and page that inherits it) to specify their own Javascript and CSS files to include and allow page.html to handle rendering them.
page.html:
<!DOCTYPE html>
<%namespace name="common" file="common.html"/>
<%
# Scan for scripts and styles to include.
include_scripts = []
include_styles = []
for ns in context.namespaces.values():
if hasattr(ns.attr, 'include_scripts'):
include_scripts.extend(ns.attr.include_scripts)
if hasattr(ns.attr, 'include_styles'):
include_styles.extend(ns.attr.include_styles)
%>
<html>
<head>
<title>${self.attr.title}</title>
% for style in include_styles:
${common.style(style)}
% endfor
% for script in include_scripts:
${common.script(script)}
% endfor
</head>
<body>
${next.main()}
</body>
</html>
common.html:
<%def name="script(src)">
<script type="application/javascript" src="%{src | h}"></script>
</%def>
<%def name="style(href)">
<link rel="stylesheet" type="text/css" href="${href | h}"/>
</%def>
index.html:
<%inherit file="page.html"/>
<%!
# Set document title.
title = "My Index"
# Set document scripts to include.
include_scripts = ['index.js']
# Set document styles to include.
include_styles = ['index.css']
%>
<%def name="main()">
<h1>${title | h}</h1>
</%def>
This all renders the following page:
<!DOCTYPE html>
<html>
<head>
<title>My Index</title>
</head>
<body>
<h1>My Index</h1>
</body>
</html>
The rendered page is missing the styles and javascript includes that I'm expecting which should be:
<!DOCTYPE html>
<html>
<head>
<title>My Index</title>
<script type="application/javascript" src="index.js"></script>
<link rel="stylesheet" type="text/css" href="index.css"/>
</head>
<body>
<h1>My Index</h1>
</body>
</html>
In page.html, if I print context.namespaces I get:
{('page_html', u'common'): <mako.runtime.TemplateNamespace object at 0x1e7d110>}
Which indicates that only the imported common.html template is available and but no descendant template namespaces which inherit from page.html. How do I iterate through the inheriting template namespaces and check their attributes? I know I can use next to get the next template namespace, but how do I get the next template namespace after that if it exists?
The code snippet in page.html to check descendent templates for the include_scripts and include_styles attributes has to traverse next of each descendant template namespace to get to the next. Using context.namespaces only appears to list the local namespaces.
import mako.runtime
# Scan for scripts and styles to include.
include_scripts = []
include_styles = []
# Start at the first descendant template.
ns = next
while isinstance(ns, mako.runtime.Namespace):
if hasattr(ns.attr, 'include_scripts'):
include_scripts.extend(ns.attr.include_scripts)
if hasattr(ns.attr, 'include_styles'):
include_styles.extend(ns.attr.include_styles)
# NOTE: If the template namespace does not have *next* set, the built
# in python function *next()* gets returned.
ns = ns.context.get('next')
Related
I tried to integrate this(having a layout.html and index.html) into my app. Before starting I only had index.html with all of my css/javascript includes at the top.
Current file struct
/app
- app_runner.py
/templates
- layout.html
- index.html
/static
/styles
- mystyle.css
Layout.html (mostly css and javascript CDN and my stylesheet)
<!doctype html>
<!-- Latest bootstrap compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional bootstrap theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- jquery -->
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<!-- Latest bootstrap compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- jstree -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<!-- my stylesheet -->
<link rel='stylesheet' type='text/css' href="{{url_for('static',filename='styles/mystyle.css')}}" />
<script type="text/javascript">
var $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
</script>
{% block body %}{% endblock %}
The page, for the most part, renders the same: The jstree appears, bootstrap works, and the rest of my styling is applied. In my css file I have a line that doesn't get applied:
td {
padding: 5px;
}
The developer console shows padding:0, which comes from a bootstrap script. If I change it in the developer console I can get it to change to 5px.
I've heard using !important is bad practice but I tried it anyway with no change. I tried adding a class to all my td so it'd have higher precedent (based on this answer) and have that style (.my_row{padding:5px;}) apply but again it doesn't change. So it seems my css isn't being applied to my table. Other parts of mystyle.css work though.
Any thoughts on why the padding isn't being applied to my table?
So it turns out my stylesheet wasn't refreshing in the cache. I found an answer on this site.
I added these lines of code to my python (app-runner.py)
#app.context_processor
def override_url_for():
return dict(url_for=dated_url_for)
def dated_url_for(endpoint, **values):
if endpoint == 'static':
filename = values.get('filename', None)
if filename:
file_path = os.path.join(app.root_path,
endpoint, filename)
values['q'] = int(os.stat(file_path).st_mtime)
return url_for(endpoint, **values)
I am just looking into using Jinja2 with a python application I have already written. I may be going about this in the wrong way, but here is what I would like to do.
from jinja2 import Environment, FileSystemLoader
from weasyprint import HTML
env = Environment(loader=FileSystemLoader('.'))
template = env.get_template("really.html")
template_vars = {"title":"TITLE","graph":'total.png'}
html_out = template.render(template_vars)
HTML(string=html_out).write_pdf("report.pdf")
This nearly produces what I want, I get a pdf called report.pdf, but instead of the attached file, it is a string of total.png. This is my first run at using Jinja, so hopefully attaching an image like this is possible. Thanks.
This is the template, not much built, just trying to do this piece at first.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h2>Graph Goes Here</h2>
{{ graph }}
</body>
</html>
I have an answer to my own question, I was simply able to add the image url into the template, without trying to pass it in as a variable.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h2>Graph Goes Here</h2>
<img src="graph.png">
</body>
</html>
Guess I was over complicating it a bit...
When using chameleon, I can replace element from a base template using the concept of slot. Where you define a slot and fill it using another tag. As there is no container element in head, how can one add elements to head ? :
The Layout file
<html>
<head>
<div metal:define-slot="extra_head"></div>
</head>
<body>
...
</body>
</html>
The content template that need to specify extra head.
<html metal:use-macro="load: main.pt">
<div metal:fill-slot="extra_head">
<script type="text/javascript" src="http://example/script.js"></script>
</div>
...
</html>
This gets rendered in :
<html>
<head>
<div metal:fill-slot="extra_head">
<script type="text/javascript" src="http://example/script.js"></script>
</div>
</head>
<body>
...
</body>
</html>
But there's no container tag in head so how can one define a slot to add stuff in the head ?
There's an alternative to using tal:omit-tag (which I'm finding annoyingly confusing - more than once I spent many minutes trying to figure out why a certain tag does not appear in the output when it's clearly present in the template, only to find tal:omit-tag neatly tucked in the far corner): if you use xml tags with tal: and metal: namespaces they won't appear in the output:
<html>
<head>
<metal:my-slot define-slot="extra_head"></metal:my-slot>
</head>
<body>
...
</body>
</html>
and in the child template:
<metal:template use-macro="load: main.pt">
<metal:any-descriptive-name fill-slot="extra_head">
<script type="text/javascript" src="http://example/script.js"></script>
</metal:any-descriptive-name>
...
</metal:template>
Note how the template becomes much more readable and self-descriptive and does not contain weird things such as a <div> inside <head> :)
You also can omit tal: and metal: prefixes on attributes when using namespaced tags, so
<h1 tal:condition="..." tal:content="..." tal:omit-tag="">Hi there! Bogus content for added confusion!</h1>
becomes
<tal:greeting condition="..." content="..." />
To remove the tag one has to use tal:omit-tag :
In the content template, use :
<html metal:use-macro="load: main.pt">
<div metal:fill-slot="extra_head" tal:omit-tag="">
<script type="text/javascript" src="http://example/script.js"></script>
</div>
...
</html>
The div is not part of the result. Read the doc.
I have set up a website using webpy.
I have my main page called layout.html. I load foo1.html into layout
$def with (content)
<html>
<head>
<title>Foo</title>
</head>
<body>
$:content
</body>
</html>
And the content inside is foo1.html
<div> Hello </div>
Is it possible to change foo1.html to also load another webpage:
$def with (secondarycontent)
<div> $:secondarycontent </div>
Just define render as template global
template_globals = {}
render_partial = template.render(template_dir, globals=template_globals)
render = template.render(template_dir, globals=template_globals,
base='layout')
template_globals.update(render=render_partial)
So now you can call it from templates
$:render.nested.template()
I have a base.mako template with a if statement to include or not jQuery
<head>
% if getattr(c, 'includeJQuery', False):
<script type="text/javascript" src="jquery.js"></script>
% endif
...
Several templates inherit from base.mako, someone needs jQuery, someone don't.
At the moment I have to set the attribute in the controller before calling render
c.includeJQuery = True
return render('/jQueryTemplate.mako')
but I think this should go directly in child template (i.e. jQueryTemplate.mako)
I tried adding it before inherit
<% c.includeJQuery = True %>
<%inherit file="/base.mako"/>\
but it does not work.
Any tips?
Thanks for your support
You shouldn't be using "c" in your template.
<% includeJquery = True %>
and
% if includeJquery:
...
% endif
should suffice.
I think you are doing this wrong... In your base template you should make a blank def for a jquery block and call it. Then in the inherited template just redefine the block.
base.mako:
<head>
${self.jquery()}
</head>
<%def name="jquery()"></%def>
Then in another template you add jquery with:
<%inherit file="base.mako />
<%def name="jquery()">
<script type="text/javascript" src="/js/jquery-1.4.2.min.js"></script>
</%def>
Well, since with the line
<script type="text/javascript" src="jquery.js"></script>
I also need to add some other js I put a jQueryScript %def in child template
##jQueryTemplate.mako
<%def name="jQueryScript()">
<script>
</script>
</%def>
then in base I check if exists and add all accordingly
#base.mako
%if hasattr(next, 'jQueryScript'):
<script type="text/javascript" src="/js/jquery-1.4.2.min.js"></script>
${next.jQueryScript()}
%endif
so I don't need to set nothing in the controller.