Django template blocks: how can i reuse them and how do i pass HTML into them? - django

Being a frontend dev familiar with Ruby, i'm trying to learn Django templating system.
It appears to be an inside-out version of what i'm used to. I struggle to comprehend its reverse ideology: instead of declaring reusable blocks and including them where necessary, in Django you mark some parts of your template as overridable.
Here are two things that i don't understand about this.
With Ruby's Padrino, i would declare a partial (a reusable snippet of templated HTML) and then include it in multiple places. Wherever i call it, it would output its HTML.
According to Django's templating documentation, each block can be used on a page only once: Finally, note that you can’t define multiple block tags with the same name in the same template.
Another feature of Padrino that i find extermely useful is that partials can accept HTML and output (yield) it in a certain place inside them. Below are a couple examples, one for Padrino and one for Jade.
Please note that partails HTML not as a string (awkwardly passed via an argument) but in a template language via nesting.
Padrino (Ruby) example
With Padrino i can pass HTML template code into partials:
_container.erb
<div class="container <%= myclass %>">
<div class="container-inner">
<%= yield %>
</div>
</div>
layout.erb
<%= partial 'container', locals: { myclass: 'container--header' } do %>
<h1><%= Sitename %></h1>
<p>Welcome to my humble place</p>
<% end %>
Resulting HTML
<div class="container container--header">
<div class="container-inner">
<h1>Sitename</h1>
<p>Welcome to my humble place</p>
</div>
</div>
Jade example
In Jade, partials are called mixins and are processed directly by the template engine rather than the backend framework:
Jade source
mixin article(title)
.article
.article-wrapper
h1= title
if block
block
else
p No content provided
+article('Hello world')
p This is my
p Amazing article
Resulting HTML
<div class="article">
<div class="article-wrapper">
<h1>Hello world</h1>
<p>This is my</p>
<p>Amazing article</p>
</div>
</div>
Is it possible with Django?
Questions:
How do i reuse a block multiple times in Django? I would like to declare a snippet of template code and include it in multiple places on the page.
How do i pass HTML (template code) into a block? I would like to reuse it with different content.
The use case that i'm trying to cover is a reusable partial/mixin/block that would serve as a container wrapper for each section of the site.
Note that with Padrino, i can even make the partial in such a way that it will let me choose which wrapper tag (div, nav, header...) should be used for each instance of the partial, by passing an argument when including the partial:
<% partial 'container', myclass: 'container--header', tag: 'nav' %>
I wonder how to do that with Django.
In your answer, please comment on whether it is possible with both a) basic Django functionality; b) some Django extensions.
Thank you.

I’m not familiar with Padrino, so I’m not 100% sure I understand what you’re looking for.
However, Django template blocks definitely aren’t the equivalent of Padrino’s partials. From your description, I think the equivalent would be custom template tags.
In outline, to create a custom template tag, you:
Create a templatetags module within your Django app, with a file in it to contain the Python code for the tags e.g.
yourapp/
models.py
templatetags/
__init__.py
mytemplatetags.py
Within that file, create a variable called register that’s an instance of django.template.Library:
# mytemplatetags.py
from django import template
register = template.Library()
Within that file, write a function for each custom tag that you want.
For a tag that includes a template snippet, you’d want to write an inclusion tag.
Inclusion tags can take arguments, which could include HTML (but only as a string).

Related

Extending template blocks in Phoenix framework for custom css/js in the templates [duplicate]

I am looking to add additional layout parameters like the #inner for the layout. For example #title for the <title>#title</title> and an area for onload javascript for individual pages.
window.onload = function () {
#onload_js
}
These are set in the layout, so I am not sure the best way to handle these in Phoenix. Thanks :D.
For the page title, you can simply pass a value through from your controller:
def edit(conn, params) do
render(conn, "edit.html", page_title: "Edit The Thing")
end
<head>
<title><%= assigns[:page_title] || "Default Title" %></title>
</head>
Note that this uses assigns[:page_title] instead of #page_title or assigns.page_title as they will error if the :page_title key is not present in assigns.
For including templates (your script example) there is render_existing/3 (and the docs for the same function in the latest version of Phoenix).
The documentation gives a similar example to what you requested so I have copied it here for convenience:
Consider the case where the application layout allows views to dynamically render a section of script tags in the head of the document. Some views may wish to inject certain scripts, while others will not.
<head>
<%= render_existing view_module(#conn), "scripts.html", assigns %>
</head>
Then the module for the #inner view can decide to provide scripts with either a precompiled template, or by implementing the function directly, ie:
def render("scripts.html", _assigns) do
"<script src="...">"
end
To use a precompiled template, create a scripts.html.eex file in the templates directory for the corresponding view you want it to render for. For example, for the UserView, create the scripts.html.eex file at web/templates/user/.

String manipulation in Django templates

Imagine the context variable {{ url }} outputs www.example.com/<uuid-string> where <uuid-string> is different every time, whereas the former part of the URL stays the same.
Can one change the output of {{ url }} to instead www.forexample.com/<uuid-string>, via solely manipulating the string in the template and without involving views.py (which I know is the better way to do it, but that's not the question).
An illustrative example would be great.
read about filters and templatetags - they are a methods that allows you to perform some actions on variables in templates.
You can also create your own tags and filters that allow you to perform action non-built into Django template language
Simple example of such filter:
#in templatetags.py
#register.filter(name='duplicate')
def duplicate(value):
return value*2
#in your template
<p> {{ url|duplicate }} </p>
You can find more examples here. Also there you will find tutorial how to use and create them

Template Contexts not recognized from external file (Django)

So, I've been editing a website and have many JavaScript functions that utilize the Contexts that the views.py file passes to the page. Until now, these functions have been contained in the base.html file and so have been loaded onto every page directly. However, to make things cleaner, I copy and pasted all the functions to an external .js file. Now, rather than use the contexts, the functions consider them to be literal strings.
Example:
$('#title').text('{{ event.name }}');
The above line will actually set the text of the element to say "{{ event.name }}" rather than the name of the event. Any ideas on how to fix this? I really don't want to keep these functions in the base file where they can be seen by anyone who inspects the page source.
It doesn't matter if you put your javascript functions in an external file or in your base.html it would still get exposed to the user. Only a minification of the file would actually help to trick the user from seeing the actual values but with javascript all your code is public.
Why you're having this problem is because when you rendered the javascript inline (in your base.html) you had access to the template context.
This is no longer the case and the Django template engine doesn't interpolate your {{ event.name }} anymore.
The problem you're facing as well is a good one. You should never mix and match javascript with Djangos template language or any template language for that matter and the only way of fixing it is to
a) start pulling the values from the DOM ie. render a proper DOM
b) to start to fetch the values from the server, traditionally using AJAX.
And the smallest example that I can muster at the moment is below:
Your view:
def my_django_view(request):
return HttpResponse(json.dumps({'meaningoflife':42}), mimetype='application/json')
Your HTML
<input type="hidden" id="myMeaning" value="{{ meaningoflife }}" />
Your javascript
var meaning = document.querySelector('#myMeaning').value;
alert(meaning); //should alert 42.
In your view you return some form of render_to_response which takes a template argument and a context argument. What the render_to_response function does is read your template, and replace all {{ placeholders }} with the values passed via the context dictionary.
Templates are essentially a complex version of this
"""
<h1>{{ person.name }}</h1>
<p>{{ person.phone_number }}</p>
""".format(person)
The problem is the templating engine does not know files specified by a scripts src attribute is actually a Django template. To fix this don't use the script src attribute. Instead do something like this.
<!--base.html-->
<h1>Site Title</h1>
<p>Some content</p>
<script>
{% include 'jsfile.js' %}
</script>
Using the include statement should do the trick.

Have separate template for each tab without having separate URL - Django

I'm trying to develop a reporting system using Django. I have to display reports about various categories of data.I have put each category as a tab-tab1,tab2, etc. Is it possible to have different template for each tab without having to change the url.
I have tried template inheritance but that requires have separate url for each tab.
My concern is that if the number of tabs grow, then the number of urls will also increase.
Any suggestions please?
Thanks in Advance.
Why is it a problem for the number of URLs to increase?
Presumably you don't need separate URLconf entries for each tab, you can just capture the tab name in the URL and send it on to the view:
url(r'^reports/(?P<tab_name>\w+)/$', views.reports, name='reports')
...
def reports(request, tab_name):
... do something depending on tab_name ...
You can just use {% include %} tag and include different templates.
And I think it's better to have unique URL for each tab, it least with hashtag.
You can use a library like jquery tabs to create the tabs, then load each template individually either through include as suggested by #DrTyrsa or by a custom template tag (which would be my personal preference).
Here is an example (from the excellent bootstrap framework from twitter):
<ul class="tabs">
<li class="active">Home</li>
<li>Profile</li>
<li>Messages</li>
<li>Settings</li>
</ul>
<div class="pill-content">
<div class="active" id="home">...</div>
<div id="profile">...</div>
<div id="messages">...</div>
<div id="settings">...</div>
</div>
<script>
$(function () {
$('.tabs').tabs()
})
</script>

How do I include raw HTML files in Symfony2/Twig templates?

I'm working on a project in Symfony2 and I have several small pieces of html that need to be included in one of my main views. According to the official Twig documentation I should be able to simply use {% include 'filename.html' %} but in Symfony unless the filename ends in ".html.twig" it throws and error saying it cannot find the file. I'd like to avoid using Twig templates for these files since they have no dynamic content and lots of double braces (they're javascript templates) and requiring the template designer to have to wrap every one of these files in {% raw %} tags seems like a really Kludgey way to do it.
I also came upon the same problem trying to find a solution to include files (mustache templates) as raw in Twig templates so Twig doesn't try to parse them.
At first I had my mustache template files named simply sometemplate.html and wrapped in {% raw %} tags. This worked for a while, but then I started using PhpStorm IDE with the Handlebars plugin (for mustache syntax). For PhpStorm to recognize the files as mustache syntax, they need to have a unique file extension (.mustache by default), so I renamed my sometemplate.html to sometemplate.mustache but I really disliked the idea that my mustache templates needed to be wrapped with Twig tags. So I ended up doing what #rdjs said in his option 3. This is the best solution imo.
Here's the working Twig extension function I made:
function twig_include_raw(Twig_Environment $env, $template) {
return $env->getLoader()->getSource($template);
}
$twig->addFunction('include_raw', new Twig_Function_Function('twig_include_raw', array('needs_environment' => true)));
With this in place you can easily include files as "raw" without Twig parsing them by doing:
{{ include_raw('sometemplate.mustache')|raw }}
I even made a Twig macro for simplifying including mustache templates to HTML head sections:
{% macro mustache_script(id, file) -%}
<script id="{{ id }}" type="text/x-mustache-template">
{{ include_raw(file)|raw }}
</script>
{%- endmacro %}
And after importing the file with the above macro to your Twig template ({% import "macros.twig" %} for example), you can easily import mustache template files in your Twig templates by simply doing {{ mustache_script('sometemplate_tpl', 'sometemplate.mustache') }} inside a HTML <head> section.
I hope this helps someone who's looking for a solution to the same problem.
A quick recap on twig file extensions (taken from the documentation):
Every template name also has two extensions that specify the format and engine for that template.
AcmeBlogBundle:Blog:index.html.twig - HTML format, Twig engine
AcmeBlogBundle:Blog:index.html.php - HTML format, PHP engine
AcmeBlogBundle:Blog:index.css.twig - CSS format, Twig engine
By default, any Symfony2 template can be written in either Twig or PHP, and the last part of the extension (e.g. .twig or .php) specifies which of these two engines should be used. The first part of the extension, (e.g. .html, .css, etc) is the final format that the template will generate.
Therefore it makes sense to me that including a file as .html would be at the least ambiguous even if it didn't throw an error.
So you have 3 choices:
If the files are purely javascript then include them as script tags in your page.
If they are mixed HTML and JS then escape the JS with {% raw %} and include the files as foo.html.twig templates. If there are lots of scripts being included like this then most likely your designers could do with a little refactoring and move the bulk of their scripts to external files (see option 1)
If you really insist you could always write a Twig extension to include raw HTML files. (EDIT: See #Haprog's answer below for more details on this option).
{{ include_html('foo/bar.html') }}
UPDATE 2015 twig has since added the source function:
{{ source('AcmeSomeBundle:Default:somefile.html.twig') }}
Kudos to #Nigel Angel in the comments below for option 4.
I came accross this post, as I had a similar question. After an hour or so searching and trying, I found out, that as from Twig Version 1.15 the "source Function" was added.
Maybe that helps someone in the future.
Follow up on Kari, if you're in an extension.. you can apply it this way.
public function getFunctions() {
return [
'include_raw' => new \Twig_Function_Method($this, 'twig_include_raw', array('needs_environment'=> true, 'is_safe'=> array('all')))
];
}
And that'd be a $this->twig_include_raw type method. You'd include within your template as:
{{ include_raw("my/file/here.html.twig") }}
No need for " | raw".