web.py and how to use section blocks - python

I use web.py Templator for my project and using the render('templates', base="") I combine a base layout with a page specific layout (simplified).
view.py
render = web.template.render('templates',base='layout')
return render.index()
shared layout file
layout.html
$def with (content)
<html>
<head>
<title>$content.title</title>
</head>
<body>
$:content
</body>
</html>
page specific template
index.html
$def with (values)
$var title: Hello Kitty
<p>Hello $values, how are you doin?</p>
The solution I'm looking for is how to achieve the following
login.html
$def with (values)
$var title: Enter credentials
<form>
<p><input type="text" name="user_name"></p>
<p><input type="password" name="user_pwd"></p>
<p><button type="submit">Open the gates</button></p>
</form>
$block_begin
<script>
// When the form is submitted, check the required fields and inform user
// if any data is missing or looks weird
</script>
$block_end
</body>
</html>
My question is, how do I add the script to the login.html template but not the index.html template? I'm not interested in having to add all JS logic to all pages, I would like to add this $block_begin/$block_end so that it appears at the bottom of the layout.html like this
layout.html
$def with (content)
<html>
<head>
<title>$content.title</title>
</head>
<body>
$:content
$block_begin
$block_end
</body>
</html>
The $block_begin/$block_end was just something I came up with to better explain myself.

Just to be clear, template -> defwith sections is a grammar, not an example. To use templates, check http://webpy.org/docs/0.3/templetor for examples.
At a high level, you create templates similar to
=== templates/index.html ===
$def with(values)
$var title: Hello Kitty
<p>Hello $values, how are you doin?</p>
=== templates/layout.html ===
$def with(content)
<html>
<head>
<title>$content.title</title>
</head>
<body>
$:content
</body>
</html>
Then in your python you render the template, passing in any parameters specified in the template. ("values" in this example.) The named template (index.html) is render using the base (layout.html), and as you've discovered, content contains the rendered internal bit (the results of index) and is inserted into the base template, layout.
You're asking how to get some script into login.html, but not in index.html & that's easy: just add the javascript code into the login.html template.
=== login.html ===
$def with (values)
$var title: Enter credentials
<form>
...
</form>
<script>
window.addEventListener('load', function () {
// whatever javascript you want to execute on load.
// If using jQuery, you'll have to use $$('form') or jQuery('form') rather
// than $('form'), as dollar signs are special within the template.
});
</script>
Something more clever? Use content more fully. Anything you define using $var in your template gets put into $content in the base layout.
If you want to include login.js only when your login.html page is rendered, you could simple create a new content attribute. In login.html:
$var extra_js: js/login.js
Then, in your layout file conditionally load the value at the bottom (where we like to load scripts).
=== templates/layout.html ===
...
<body>
$:content
$if content.get('extra_js', None):
<script type="text/javascript" src="$content.extra_js"></script>
</body>
</html>
You can make your layout.html more and more powerful, parameterizing meta data, scripts, css files, etc. Just like you did with $content.title, and let your individual template files drive different parts of the overall layout.

Related

Scan descendant template namespaces for particular attributes

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')

Configurable head with chameleon load

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.

Having multiple templates nested within each other using weby

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()

Web Form with Web.py on PythonAnywhere

I'm trying to make a simple web script using the PythonAnywhere.com's web.py platform.
My intention is to create a simples Form which gets the data of textboxes and is able to work with them just like we do with PHP and so.
This is my main script:
import web
from web import form
import MySQLdb
render = web.template.render('/home/user/templates/')
conn = MySQLdb.connect("mysql.server","user","*********","userdb")
curs = conn.cursor()
curs.execute('''create table if not exists Dados (
id int not null auto_increment primary key,
nome varchar(200),
item1 varchar(50),
item2 varchar(50),
item3 varchar(50),
item4 varchar(50),
item5 varchar(50));
''')
urls = (
'/', 'index'
)
formula = form.Form(
form.Textbox('Nome', id='nome'),
form.Textbox('Item 1', id='it1'),
form.Textbox('Item 2', id='it2'),
form.Textbox('Item 3', id='it3'),
form.Textbox('Item 4', id='it4'),
form.Textbox('Item 5', id='it5'),
)
class index:
def GET(self):
form = formula()
return render.formtest(form)
def POST(self):
form = formula()
return render.finaliza(form['Nome'].value)
# comment out these two lines if you want to use another framework
app = web.application(urls, globals())
application = app.wsgifunc()
Then I have two templates in HTML, this one stores the Form:
$def with (form)
<form name="main" method="post">
$:form.render()
<input type="submit" name="send" id="envia" value="Ok" /> </form>
And this should give the result after the POST:
$def with (nome)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body>
Congratulations $:nome !
</body>
</html>
Everything works fine until a press the Ok button.
It shows the right template but doesn't show the $nome variable.
You can check this behavior on this link:
http://jonathan_hepp.pythonanywhere.com/
I'm beginning on web.py and PythonAnywhere so there must be something I'm doing wrong but I can't find it out.
Could you please help me out?
Thanks.
EDIT:
I've just find out now that if I pass the textbox value as a string the result is different.
The result page says "Congratulations None!"
That makes me think that actually the POST is not recieving the value I'm typing in the textbox. So the code seems to be ok but somehow I didn't make it right so it cannot reach the value in the form's input.
Still not working.
SOLVED:
Ok. I realized that the form.Form() option doesn't really create and html form output.
Actually when you look at the source code of the formtest page you see that what I supposed was the form appears as a simple .
So I just made the form into the formtest template directly in html an now it works pretty well.
Just a dumb mistake, but if somebody else comes throught it, just do the same.
Thank you all.
It seems like the variable in the finaliza template should actually be "Nome". So:
Congratulations $:Nome !
Might do the trick. Remember to restart your web app on the Web tab of PythonAnywhere after making changes or you won't see them.
I am using the same web.py framework. when I try to access it via localhost and URL after is /templates/tutorial.html
then I can see "send" button but with these $ signs
$def with (form, text)
$:form.render()
$text
Here is tutorial.html
$def with (form, text)
<head>
<title>Python and AJAX tutorial for beginners with webpy and jQuery</title>
<link rel="stylesheet" type="text/css" href="/static/tutorial.css" />
<script type="text/javascript" src="/static/jquery.js"></script>
<script type="text/javascript">
jQuery(document).ready(function() {
jQuery(".button").click(function() {
var input_string = $$("input#textfield").val();
jQuery.ajax({
type: "POST",
data: {textfield : input_string},
success: function(data) {
jQuery('#foo').html(data).hide().fadeIn(1500);
},
});
return false;
});
});
</script>
</head>
<body>
<br>
<form class="form" method="post">
$:form.render()
<input class="button" type="submit" value="send"/>
</form>
<br><br>
<span id="foo">$text</span>
</body>
How I can get the values of these variables ?
$def with (form, text)
$:form.render()
$text

Condition mako base template from inheriting one

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.

Categories

Resources