Flask/Jinja2 - Include template relative to current template - python

Is there a syntax for referencing a local file? For example
Instead of having to write :
{% include 'user/project/task/task_detail/tab.html' %}
I want to write (from a template that's in the same directory as tab.html):
{% include './tab.html' %}
But the above raises jinja2.exceptions.TemplateNotFound

You can subclass Jinja's Environment and override the join_path() method to support import paths relative to the current directory.
class RelativeEnvironment(jinja2.Environment):
"""Override join_path() to enable relative template paths."""
def join_path(self, template, parent):
return os.path.join(os.path.dirname(parent), template)
However please mind that Jinja's mechanism of finding templates is designed in order to disambiguate templates' paths.
You can read more at https://jinja.palletsprojects.com/en/2.10.x/api/#jinja2.Environment.join_path
This question is a possible duplicate of How to include a template with relative path in Jinja2

Flask/j2 needs to know the structure to find a file, and it knowns the basic structure of app subfolders (static, templates etc.).
The best way is to put it in a static subfolders and then point at it using the url_for function:
..."{{ url_for('static', filename='<subfolder>/tab.html') }}"...
`

Related

Is it possible to use Bottle's/Flask's statement and functions from within a template?

I was wondering if i can use the static_file() from withing a template as follows:
{{ static_file( filename, root=filepath, download=True ) }}
Can it be written like that?
And if can, then how i will be able to substitite the variables within the statement, in my case, filename and filepath with their values taken from the wsgi python file?
And in general, are we able to use Bottle's framework statement and functions from within a template system or we can use then from the wsgi python app?!
No, static_file is for returning a static file; templates are for returning dynamic content. They do not interoperate.
You use static_file from within your code (route handler), not from within a template.
E.g. (from the documentation),
#route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/path/to/your/static/files')
Static file is a routing option, it doesn't actually serve files. You can just return an open files binary data however using a function and use javascript to prompt the download.

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.

Locating lower level templates

Hi I am having problems with the template using Bottle
my folder structure is this:
|views
--main.tpl
--|blog
--home.tpl
what I want to do is this:
def home():
return template('blog/home')
but it won't work
I can get it to work just calling the following:
def home():
return template('main')
But I want to be-able to have many different folders
I understand that I will still need to keep unique names because of the caching
and please don't say use a different framework as this is not my choice.
You can try passing the template_lookup argument to the template function. template_lookup overrides the defaults .views path when looking for the template. However I believe this will only work if the name of the tempalte is not in the views folder. So if you had a /views/main.tpl and a /blog/main.tpl it would not work, every template needs a unique name. This is needed because bottle will only lookup search for tempaltes if it hasn't found it before and stores the found ones in a dict with the tempalte name as the key. so if the templates have the same name it would use the first one.
return template("home", template_lookup="full_path_to/views/blog/"

Using Django view variables inside templates

this is a rather basic question (I'm new to Django) but I'm having trouble using a variable set in my view inside my template. If I initialize a string or list inside my view (i.e. h = "hello") and then attempt to call it inside a template:
{{ h }}
there is neither output nor errors. Similarly, if I try to use a variable inside my template that doesn't exist:
{{ asdfdsadf }}
there is again no error reported. Is this normal? And how can I use my variables within my templates. Thanks!
In order to have access to a variable in a template, it needs to be in the the context used to render that template. My guess is you aren't passing a context dictionary to the template when you render it.
http://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render-to-response
The "dictionary" referenced there is a dictionary that contains all the variables you want to have available in the context. For example:
return render_to_response('your_template.html', {'h': h})
As far as the "no error" error goes... That's the default setting for an invalid template variable. You can change that in the project's settings if you'd like.
http://docs.djangoproject.com/en/dev/ref/settings/#template-string-if-invalid
You can also use
return render(request, 'your_template.html', {'h':h, 'var1':var1})
Refer to the latest manual on https://docs.djangoproject.com/es/1.9/topics/http/shortcuts/
Yes! This is normal. Such errors in templates fail silently and this is expected in Django.
to render properly template use render_to_response('your_template.html', {'h':h}) (there is also a nasty shortcut render_to_response('your_template.html', locals()) if your context dictionary is very big)
here is some explanation with examples: http://www.djangobook.com/en/beta/chapter04/ (section 'How invalid variables are handled')

Django: specifying a base template by directory

I'm working on a Django site that has multiple sections and subsections. I'd like to have several depths of template inheritance: a base template for the whole site, one base template for each section that inherits from the root base template, and so on. Here's a simplified version of my desired directory structure:
base.html
section1/
base.html
section2/
base.html
section3/
base.html
What I would desire is for all the files under section1/ to contain something like {% extends "base.html" %}, meaning they would extend section1/base.html. section1/base.html would contain something like {% extends "../base.html" %}, meaning that it would extend the root-level base file. However, I couldn't find anything in the documentation suggesting this was possible, and I couldn't get Django to distinguish between "../base.html" and "base.html". ({% extends "../base.html" %} throws an error.) I suppose one workaround would be to rename all base files base_SECTIONNAME.html, and update all the files that inherit from them, but I am concerned that this might become difficult to maintain as my site becomes bigger and sections change names, etc. I would prefer a solution that takes advantage of the natural hierarchy specified by directories and subdirectories.
Any ideas?
May be I oversee something, but all you want can be accomplished with the django template system. All extends calls are relative to template directories.
In order for all base.html files in subdirectories to extend base.html, you just have to put a {% extends "base.html" %} into the files. section1/base.html would would look like that.
{% extends "base.html" %}
{# ... rest of your code ...#}
Now, to get the files from section1 to extend section1/base.html you just have to put {% extends "section1/base.html" %} at the top of them. Same for section2, section3 and so on.
It is just that simple, but might not totally obvious in the documentation.
I hope, I understood your question.
The accepted answer will work, but I do recommend using variable names to keep track of section structure. My personal preference would be a context processor. If, for example, your site's section organization is transparently reflected in the url, try something like:
# It may be convenient to make this function live in or near your url conf.
def convert_url_path_to_folder_path(path):
# fill in the magic here
def sub_folder_available(request):
folder = convert_url_path_to_folder_path(request.path)
return {'subsection': folder, 'local_base':folder+'/base.html'}
Then in your template, just call
{% extends local_base %}
There are probably a dozen other ways to do this, but the main thing is to think about avoiding hard-coding the folder name into the template. This will get you a lot of mileage, especially since you can just drag and drop template between sections if they happen to be similar enough. Another thing you might add insert is:
def sub_folder_available(request):
folder = convert_url_path_to_folder_path(request.path)
# Check if local base exists:
if os.access(folder+'/base.html',os.F_OK):
base = folder+'/base.html'
else:
# revert to your global base
base = 'base.html'
return {'subsection': folder, 'base':base}
The nice advantage of this strategy is of course that you can get a fly-weight section up and running without any local base template at all.
You can use this library: https://github.com/vb64/django.templates.relative.path
Just write in your templates as follows:
{% load relative_path %}
{% extends ".base.html" %}
this will extend template "base.html", located in the same folder, where your template placed
{% load relative_path %}
{% extends "...base.html" %}
extend template "base.html", located at two levels higher
same things works with 'include' tag.

Categories