When inside a jinja template:
How is the string, provided to {%extends xxx_string%} and {% include xxx_string%}, resolved?
Is this relative to actual filesystem, or a generated namespace (such as when using the Flask.url_for function)?
Ultimately, I would like to use relative imports when inside my templates (I don't want to have to update filesystem locations INSIDE each and every template, with respect to the Blueprint). I would like to be able to :
Store the actual Blueprint package and its nested static/template resources under an arbitrary filesystem path. ('/bob/projects/2013_07/marketanalysis')
Within the python Blueprint package, define a separate 'slugname' to reference the blueprint instance and all of its resource. Register this slugname on the application for global references. (without global name collisions or race-conditions)
Have generic view functions that provide 'cookie-cutter' layouts, depending on how the blueprint is being used (headlines, cover, intro, fullstory, citations)
Internally, within the filesystem of the blueprint package, use relative pathnames when resolving extends()/include() inside templates (akin to url_for shortcuts when referencing relative blueprint views).
The idea is that when the blueprint package is bundled with all of its resources, it has no idea where it will be deployed, and may be relocated several times under different slug-names. The python interface should be the same for every "bundle", but the html content, css, javascript, and images/downloads will be unique for each bundle.
I have sharpened the question quite a bit. I think this is as far as it should go on this thread.
Using folders instead of prefixes makes it a bit more clean in my opinion. Example application structure:
yourapplication
|- bp143
|- templates
|- bp143
|- index.jinja
|- quiz.jinja
|- context.jinja
|- templates
|- base.jinja
|- index.jinja
|- bp143
|- context.jinja
With the above structure you can refer to templates as follows:
base.jinja --> comes from the application package
index.jinja --> comes from the application package
bp143/index.jinja --> comes from the blueprint
bp143/context.jinja --> comes from the application package (the app overrides the template of the same name in the blueprint)
Explicitly load templates using... (not tested)
#blueprint.route('/summarize_article')
def summarize():
fd = blueprint.open_resource('/blueprint/relative/pathid/section/introductions.jinja')
relative_ctx['article_intro'] = flask.render_template_string(fd.read(), **context)
fd = blueprint.open_resource('/blueprint/relative/pathid/section/citations.jinja')
relative_ctx['article_citations'] = flask.render_template_string(fd.read(), **context)
fd = blueprint.open_resource('/blueprint/relative/pathid/summarized_layout.jinja')
return flask.render_template_string(fd.read(), **relative_ctx)
Manually load these templates as needed for each view. Im sure this could be cleaned up for multiple views, but I believe this is the intended behavior.
I am not sure if you can {% extend context_kword_key %}, but it should work for embedded includes()/imports().
It seems the most appropriate solution for my "resource bundling" should be handled with Jinja loaders (see Jinja Docs on Loaders). Right away, jinja2.PackageLoader, jinja2.PrefixLoader, and jinja2.DictLoader seem like they could be fun.
The accepted answer for this Similar Thread gives an idea of how Loaders are handled in Flask. For the most part, we can stay out of the default application-level DispatchingJinjaLoader.
By default, I believe a Blueprint will end up with its self.jinja_loader to ...
jinja2.FileSystemLoader(os.path.join(self.root_path,
self.template_folder))
This help us to understand how simple the default resolution algorithm is, and how easily we can extend Blueprint specific functions. A clever combination of Subclassed/Custom Loaders will let us create smarter Loaders, and allow us to sneak in a few magics that help us cheat.
The real power will come from overriding CustomBaseLoader.list_templates() and a quick little ProxyLoader hooked into the application's DispatcherJinjaLoader that will have priority over normal lookups.
Related
The django template folder requires creating a subfolder with the name of the app which then contains the template files. Why is this necessary, when python manage.py collectstatic can infer this information while traversing all directories? It seems very redundant.
First of all, Django does not require this specific folder structure for templates to work, it is just a stablished pattern to do so. And, of course, it has a rationale, as pointed in the official doc:
Template namespacing
Now we might be able to get away with putting our templates directly
in polls/templates (rather than creating another polls subdirectory),
but it would actually be a bad idea. Django will choose the first
template it finds whose name matches, and if you had a template with
the same name in a different application, Django would be unable to
distinguish between them. We need to be able to point Django at the
right one, and the easiest way to ensure this is by namespacing them.
That is, by putting those templates inside another directory named for
the application itself.
You can reference to this question or that another for concrete cases.
In a nutshell, by following this pattern you can have your templates organized in 2 groups:
templates related to your specific site or project can live inside the directory pointed by the TEMPLATES['DIRS'] setting;
templates related to a specific app, that could be served as is if you make your app pluggable, should live inside './appname/templates/appname/' (and TEMPLATES['APP_DIRS'] must be True). This way you avoid name conflicts between files inside this folder anf files from outside.
Here is official Django tutorial, templates section:
First, create a directory called templates in your polls directory. Django will look for templates in there.
Within the templates directory you have just created, create another directory called polls, and within that create a file called index.html. In other words, your template should be at polls/templates/polls/index.html. Because of how the app_directories template loader works as described above, you can refer to this template within Django simply as polls/index.html.
...and explanation of "namespacing":
Now we might be able to get away with putting our templates directly in polls/templates (rather than creating another polls subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.
but different app has different absolute path of templates directory. Is there any problems with such project structure?
my_project
|
+--first_app
| |
| +--templates
| |
| +--index.html
| +--foo.html
+--second_app
|
+---templates
|
+index.html
We have my_project/first_app/templates/index.html and
my_project/second_app/templates/index.html - no collision.
Django will choose the first template it finds whose name matches
I suppose, Django using relative path to compare insted of absolute. What's the point of it? Is there any hidden profits?
I just diving into Django and that seems like violation of DRY principle. So, why such elegant and pythonic framework uses such weird convention?
Everything under templates/ is grouped into one list. They are "put over one another" in the order of your INSTALLED_APPS setting (the bottommost first).
In your case, if INSTALLED_APPS = ['first_app', 'second_app'], django will know of two templates:
index.html from first_app, which has overwritten the similary-named one from second_app.
foo.html from first_app.
So... everything below templates/ in each of your apps is grouped together. Which is why it is good to use the namespacing mentioned in the docs. So second_app/templates/second_app/index.html, for instance.
That Django allows you to overwrite templates is handy: you can overwrite the default login page, for instance.
I have a python project that I'm using Django templates for to generate C++ source code.
I picked Django because the template language is quite restrictive and has a very large community making it easy for end-use developers to use and get help with.
I'm failing to add custom filters for my project (to translate one set of type names into another) because I have not done the normal django setup.
Instead:
from django.template import Context, Template
import django
if not django.conf.settings.configured : django.conf.settings.configure()
django.setup()
Lets me use Django templates perfectly but not define custom filters.
My custom filter is called ctypes_filters.py and I reference it in the template as
{% load ctypes_filters %}
Running my generation script results in the following error:
django.template.base.TemplateSyntaxError: 'ctypes_filters' is not a valid tag library: Template library ctypes_filters not found, tried
django.templatetags.ctypes_filters
How can I get django to find the filter without setting up a full Django project (database definitions etc)?
I know that other templating solutions are available (and are probably more light-weight) but I'm really keen to use Django's simple and elegant templates.
The location of Django template tags is done by convention rather than a configuration setting (see the code layout section of the template tags docs).
Put the ctypes_filter.py in a templatetags directory in an installed app (I've called it myapp here). Add an empty __init__.py to both the myapp and templatetags directories. The app doesn't need any other files you might commonly find in a Django app, like models.py or views.py.
myapp/
__init__.py
templatetags/
__init__.py
ctypes_filter.py
Then include myapp in your INSTALLED_APPS when configuring your settings.
django.conf.settings.configure(
INSTALLED_APPS=('myapp',),
)
I'm building an application in Pyramid and utilizing Jinja2 templates and traversal routing. In order to wire my view-callables with the templates I am using, I want to be able to reference my templates using the webapp:templates prefix. As an example:
#view_config(name='about-us', renderer='webapp:templates/pages/about-us.html', context=Root)
def static_pages(context, request):
... //more code
This decouples where the templates live from whats using them. In order to make the above functional, though, I had to put this inside the __init__.py in my webapp root folder:
config.add_static_view(name='templates', path='webapp:templates', cache_max_age=3600)
The add_static_view() causes the webapp/templates folder to be referenced as webapp:template in other configurations. However, it also makes it viewable from a url such as http://0.0.0.0:6543/templates/<some template file>. Is there a way to achieve the former goal without allowing the latter visibility as a static page?
add_static_view() is not supposed to cause the webapp/templates folder to be referenced as webapp:template in other configurations, if it does that it's just due to a weird side-effect.
The package:path syntax works because Pyramid uses pkg_resources API to resolve the paths. Here are some details.
This means that, in your example, webapp should be a python package located somewhere your app can find it.
I am attempting to utilize Flask-Admin for an administrative interface to my web service.
I have it working, but the theme does not match what the rest of my site uses. The documentation here suggests that it is as simple as overriding the master template, but when I do that I end up with circular reference errors.
I have also tried on individual templates by copying the templates from the install directory to my application structure, but I cannot figure out the path they use. It is like it just defaults to the install directory, even if I have templates of the same name local to my flask app. From the docs: "You can override any used template in your Flask application by creating template with same name and relative path in your main templates directory."... yet I am not able to do that. Does it still expect admin/ in front of the templates?
Does anyone have an example? I basically need to override the bootstrap theme used, but some other customization could be nice. I'm new to flask, and python for that matter, so this may be quite simple...
You will still need to place your templates in the admin sub-folder of templates:
yourapp/
app.py
templates/
master.html # <-- This will not override admin/master
admin/
master.html # <-- This one, however, will :-)