Injecting values from a child template into a parent template in Jinja2 - python

I have created a Jinja2 template called lesson.html that defines the basic structure of a lesson. From that, I derive one child template for each actual lesson (since the content of each lesson is different). For example, intro.html, variables.html, lists.html, and functions.html all derive from lesson.html.
Along with slides, exercises, and what-not, each actual lesson has a YouTube video. I want to put the ID of the video in the child template file, but then put the HTML that actually references videos in the parent template (i.e., lesson.html) to ensure that every video is referenced and formatted the same way. For example, I would like lesson.html to look like:
<div class="youtube"><br/>
<iframe title="{{TITLE}}" time="{{TIME}}" src="http://www.youtube.com/embed/{{ID}}"></iframe>
<br/>
</div>
and then have TITLE, TIME, and ID all defined in the child template file itself (since that's the most logical place to store them). My current hack is to create a function that takes these values as arguments and returns a blob of HTML to be inserted in the right place:
{{youtube("Title of this Lecture", "abcde01234", "05:10")|safe}}
but (a) I'd rather use straight template expansion than function calls, and (b) the title, YouTube ID, and time aren't explicitly identified in the lesson template file (which makes them harder to extract for use elsewhere: I can do it with regular expressions, but I'd really rather not).
Is there a clean way to define values in a child template for use in a parent template?

You can use set, DOC are as follows:
Inside code blocks, you can also assign values to variables. Assignments at top level (outside of blocks, macros or loops) are exported from the template like top level macros and can be imported by other templates.
Same question can be found here.

Related

Get list of variables from Jinja2 template (parent and child)

I'm trying to get a list of variables from a Jinja2 template.
test1.j2:
some-non-relevant-content
{{var1}}
{% include 'test2.j2' %}
test2.j2:
another-text
{{var2}}
I can get variables from test1 easily:
env = Environment(loader=FileSystemLoader(searchpath='./Templates'))
src_t = env.loader.get_source(env, 'test1.j2')[0]
parsed_t = env.parse(source=src_t)
t_vars = meta.find_undeclared_variables(ast=parsed_t)
Problem is, I can only get variables from the parent template with get_source.
Obviously, I can not feed class template object to parse method as well.
Is there any way to build the full list? {'var1', 'var2'} in my case.
Ideally by using Jinja2 API. Minimum custom code.
Found a way to code that without a big pain.
meta.find_referenced_templates helps to load all child templates when applied recursively. When done, it's trivial to get variables from all templates in a single list.

Mako Template Variable Names

Is it it possible to get the names of the variables in a Mako Template before rendering?
from mako.template import Template
bar = Template("${foo}")
# something like:
# >> print bar.fields()
# ['foo']
Use case:
We have configuration files whereby we specify the metadata from the database to be displayed on a webpage. The client can pick one of a few hundred different pieces of named metadata. There are N slots the client can configure but we don't know in advance which pieces of metadata a particular client would like to have filled in on the form. Because if this when rendering the form we need to know, in advance, what variable names we need to pass for this clients template.
We had thought of having a consistent dictionary with all possible values and passing that in each time but it was unworkable as new available fields are added frequently to the underlying pool of available metadata the client could pick.
Because of this we had hoped to use Mako to template the config files but I can't figure out how to determine with the field values in the template would be so that I can build a full-formed Context to pass to the template.
Unfortunately there is no easy way to get the names of the variables from a template object.
Fortunately there is the mako.codegen._Identifiers class and the sole purpose of its objects is to keep track of the variables during the compilation process.
Unfortunately it is buried deep beneath the Mako API surface, and it is gone after the compilation is done.
Fortunately you can get it without setting up everything Mako sets up when it compiles templates. All you need is the parse tree that you can get by using mako.lexer.Lexer.
Anyway here is the code:
from mako import lexer, codegen
lexer = lexer.Lexer("${foo}", '')
node = lexer.parse()
# ^ The node is the root element for the parse tree.
# The tree contains all the data from a template
# needed for the code generation process
# Dummy compiler. _Identifiers class requires one
# but only interested in the reserved_names field
compiler = lambda: None
compiler.reserved_names = set()
identifiers = codegen._Identifiers(compiler, node)
# All template variables can be found found using this
# object but you are probably interested in the
# undeclared variables:
# >>> print identifiers.undeclared
# set(['foo'])
Chasing Mako variables is no fun. I knocked together this little function to extract the variables from a template - use & improve as you like.
def ListMakoVariables(template):
'''
Extract Mako variables from template.
'''
start = 'XXXX${'
stop = '}YYYY'
makovars = []
splitReady = template.replace('${',start).replace('}',stop)
startParts = splitReady.split('XXXX')
for startStr in startParts:
if '}' in startStr:
makovars.append(startStr.split('YYYY')[0])
vars = set(makovars)
return vars, makovars
FWIW, makovars are in-order, vars are unique but not in order.

Exposing global data and functions in Pyramid and Jinja 2 templating

I have a base template for when a user is logged in, and on that base template, I need to add user specific options in a drop down menu. This drop down menu with options must be constant across all handlers, i.e., any time the base template is invoked (extended) with a child template.
Other than performing the necessary DB query, assigning the query results to a variable, and passing that variable to every handler (there are many), how can I consolidate this into one query and one variable, which gets passed directly to the base template? I am using jinja2 templates as well.
I would hate to do something so cumbersome in exchange for something far more simple and maintainable.
Any ideas? Thanks.
EDIT
So I still haven't found anything that's exactly what I'm looking for; however, I decided to at least make some headway in the interim. So, I made a custom decorator that takes a view's returned dict() and appends the appropriate data to it. For example:
def get_base_data(func):
def wrapper(request):
d = func(request)
user_id = request.user.id # used in query
contact_group_data = ContactGroups.query.filter(...criteria...).all()
d['contact_group_data'] = contact_group_data
return d
return wrapper
Now, I can at least decorate each method very concisely and simply by putting:
#view_config(...)
#get_base_data
def my_handler(request):
pass # rest of code...
This is one of most inobvious things in Pyramid and took a while to find for me, too.
You can modify the global template context in BeforeRender event.
http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/hooks.html#using-the-before-render-event
Alternatively, you could use class based views, inherit all your views from one base view class which has get_base_data(), then the class instance is passed to the template context to all your views and then you could extract the data with {{ view.get_base_data }}.
http://ruslanspivak.com/2012/03/02/class-based-views-in-pyramid/
I vouch for the latter approach as it is more beautiful, predictable and easier to maintain engineering wise.

Can you have multiple generic subviews of a content element in Kotti?

You can add a 'view' for a content type in kotti by doing something along these lines:
from kotti_mysite.views import poll_view
config.add_view(
poll_view,
context=Poll,
name='view',
permission='view',
renderer='kotti_mysite:templates/poll.pt',
)
(more details: http://kotti.readthedocs.org/en/latest/first_steps/tut-2.html)
You can also have multiple views, and use the 'set default view', but sometimes it's convenient to have several similar views with very similar urls.
For example, in plone, its trivial to have a url structure like this:
/blah/item/ <--- Normal view
/blah/item/json <--- Json version of item
/blah/item/pdf <--- PDF download of item
You can... sort of, do a similar thing in kotti by screwing with the view you create and rendering different content based on get/post params, but it's messy, and frankly, rather rubbish.
The only solution I've found is to have a custom content type 'JsonView' that has a json renderer, and add it as a child of the parent object, and it's renderer looks for the parent content, and renders that.
However, doing this requires you to manually create a 'JsonView' child for every instance of the type you want, which is also rather cumbersome.
Is there a better way of doing this?
--
Nb. Specifically note that having a custom view /blah/item/json isn't any use at all; any type of item, in any parent folder should be able to render in the way described above; using a single static route isn't the right solution.
You can register a json view for all your content like this:
from kotti.interfaces import IContent
config.add_view(
my_json_view,
context=IContent,
name='json',
permission='view',
renderer='json',
)
This way, when you open /blah/json, where /blah points to some content, it will call your my_json_view view.
SQLAlchemy's new class object inspection system might help you write a useful generic json view that works for more than one content type. Alternatively, you can register your view for specific content types only (by use of a more specific context argument in config.add_view).
Using renderer='json' you tell Pyramid that you want to use its json renderer.

Sphinx change table of contents HTML

I'm working on theming my documentation, and I'm trying to get sphinx to add a class to the <ul> elements of the Table of Contents. Unfortunately, it looks like the HTML is generated just the same as any other list.
Is there a way to add a class to the TOC?
The class that I want to add (fyi) is nav nav-tabs nav-stacked; I'm using a bootstrap theme.
Update: my current hack is to override localtoc.html template to have
{{ toc|replace("<ul>", "<ul class='nav nav-tabs nav-stacked'>") }}
but it feels ugly...
The TOCs are converted to HTML separately and then inserted into the document, without letting you control it. Checkout the render_partial method of sphinx.builds.html.StandaloneHTMLBuilder.
Your hack is the easiest method, the other option is to subclass StandaloneHTMLBuilder and override render_partial to control the docutils' HTMLWriter.

Categories