Django shared template - django

I'm looking how to do the best something like a UserControl in ASP.NET in Django.
For example:
1) There's a Book model defined
2) There's a regular representation of the book which I want to use all over my site (called "book_template.html").
Now let's say I want to use this one representation from 2 views: recent_books_view, popular_books_view. It can be done directly like
from django import template
t = template.Template('My name is {{ name }}.')
book1_context = template.Context({'book': Book1})
book2_context = template.Context({'book': Book2})
book3_context = template.Context({'book': Book3})
...
render_to_response('recent_books.html',
{'content': t.render(book1_context) + t.render(book2_context) + t.render(book3_context)})
render_to_response('popular_books.html',
{'content': t.render(book4_context) + t.render(book5_context) + t.render(book6_context)})
But I'm sure there's a better way...
For example, in ASP.NET you can say in template file "apply for array 'Books' this shared template", and then in the backend you just specify variable 'Books'. Is that possible in Django?

In your python code:
context['books'] = blah blah # Make a list of books somehow.
return render_to_response('popular_books.html', context)
In popular_books.html:
<p>Look, books:</p>
{% for book in books %}
{% include "book.html" %}
{% endfor %}
Finally, in book.html:
<p>I am a book, my name is {{book.name}}</p>
There are more interesting ways to modularize, such as creating a custom tag, so that you could, for example:
<p>Look, books:</p>
{% for b in books %}
{% book b %}
{% endfor %}

I think you're looking for this tutorial Chapter 4 of the Django book: The Django Template System.
See block tags and template inheritance.

Related

Drupal 8: List taxonomy terms with active class in twig

Let say I have a taxonomy name Flavor and the terms have Vanilla, Chocolate and Green Tea.
I also have a content type name ice-cream with flavor field under 'Flavor' taxonomy which can multiple select.
I create a ice-cream page name Baskin Robin and I tick 'Vanilla' and 'Chocolate' under flavor field.
How to list all 'Flavor' terms in node.html.twig and add a class for 'Vanilla' and 'Chocolate'?
For example:
<div class="field-item active">Vanilla</div>
<div class="field-item active">Chocolate</div>
<div class="field-item">Green Tea</div>
You need a block or a views to do that.
You can also use a hook_preprocess_node to inject all flavors in the ice-cream template.
Here the documentation to create a custom block.
Here the documentation to create a views block.
Here the documentation to place a block (custom or views).
I will explain the easiest & fastest, but most dirty one, to achieve your goal - using the hook_preprocess_node.
I recommend to use the custom block or the views for scalability & maintainability. The code below is a working example, you could use it in a custom block.
Here the code using the preprocess way.
In your template.theme file:
/**
* Implements hook_preprocess_node.
*/
function template_preprocess_node(&$variables) {
$node = $variables['node'];
if($node->bundle() == 'ice-cream') {
// Should be injected.
$termStorage = \Drupal::entityManager()->getStorage('taxonomy_term');
// Load all flavors taxonomy term.
$variables['flavors'] = $termStorage->loadTree('flavors', 0, NULL, TRUE);
}
}
In your node.html.twig or node--ice-cream.html.twig:
{% for flavor in flavors %}
{% set classes = ['field-item'] %}
{% for node_flavor in node.field_flavors if node_flavor.target_id == flavor.id %}
{% set classes = classes|merge(['active'])%}
{% endfor %}
<div class="{{ classes|join(' ') }}">{{ flavor.name.value }}</div>
{% endfor %}
Hope it will help you !

WagtailCMS Multisite Site Configuration

I am building a non profit association website and am using the Sites feature to build custom templates for their chapter sites. What I am trying to do is re-use the templates by targeting the Sites name or id or... For instance, if you look at the screenshot below, I have two sites so far I am testing and I have a model called StandardPage(Page) and a matching template standard_page.html. The main site will look slightly different than the chapter sites, but I would like to re-use the standard_page.html template for both with an if path is this, display this, if not this...
I know this isn't correct, but to illustrate what I am trying to do:
standard_page.html
{% if request.path != '/' %}
{% include 'includes/chapter/_chapter-standard-page-content.html' %}
{% else %}
{% include 'includes/ao/_ao-standard-page-content.html' %}
{% endif %}
They have over 50 chapters around the world, so you can see how important it would be to be able to re-use templates so I am not having to created each template for each model for each chapter .
Thank you.

Django template variable containing template tag, ex {{ {% some_tag %} }}

I have a template that receives a list context variable, tags_list. I need to iterate over this list 'inserting' the tags in the template something like this:
{% for tag in tags_list %}
{{ tag.tag }}
{% endfor %}
When this renders it returns the text value of tag.tag, "{% tagxxx %}", not the rendered tag.
How can I cause the template render to render the value of a context variable? Alternately, is there a filter, a sort of inverse verbatim, that will cause the value of a context variable to be rendered?
Updated background
tags_list is created by a fairly sophisticated process involving exec of some user provided text from a table/model field. The relevant portion of the real template looks like this:
{% for graph_row in graph_rows %}
<div class="row">
{% for graph in graph_row %}
<div class="col-md-{{ graph.width }}">
{{ graph.graph }}
</div>
{% endfor %}
</div>
{% endfor %}
The graph values look like this: {'graph':'{% piechart data1 %}', 'width':3}
Note that the order of entries in the context variable graph_rows is significant as is order of graph(s) in the row as that determines the placement of graphs on the page. Preserving this order is essential for the scheme to work correctly.
Currently, the view function simply does an {% include ... %} to get the template segment above to render in the correct order. This approach is simple and clean.
I could, as has been suggested, perform a template render within the view function but that complicates the design a bit and I'd hoped to avoid doing that if there is an easy way to trigger a render of {{ graph.graph }}. Note, as well, by moving the render into the view I loose the ability to easily take the template from arbitrary places, in particular table fields.
One of the great things about Django is the library of solution and code snippets. Sadly, they aren't a well organized and easy to find as one might wish. Nevertheless, a bit of google found a number of solutions of the general form
{% render tag.tag %}
Here are links to several:
render_as_template template tag
Allow template tags in a Flatpage's content
render_as_template.py
I'll use the general approach cleaned up a bit for error checking.
As an aside, the technique strikes me as generally useful and might be appropriate for inclusion in the standard tags.
Update 3/28/2014
After looking at the above and several others this is what I used from render_as_template template tag. There is a useful comment here.
from django import template
from django.template import Template, Variable, TemplateSyntaxError
register = template.Library()
class RenderAsTemplateNode(template.Node):
def __init__(self, item_to_be_rendered):
self.item_to_be_rendered = Variable(item_to_be_rendered)
def render(self, context):
try:
actual_item = self.item_to_be_rendered.resolve(context)
return Template(actual_item).render(context)
except template.VariableDoesNotExist:
return ''
def render_as_template(parser, token):
bits = token.split_contents()
if len(bits) !=2:
raise TemplateSyntaxError("'%s' takes only one argument"
" (a variable representing a template to render)" % bits[0])
return RenderAsTemplateNode(bits[1])
render_as_template = register.tag(render_as_template)
This gets part of the way to a solution. Unfortunately custom template tags, in my case
{% pie_chart %} are not available to render within the class RenderAsTemplateNode.
I've not tested this but it appears that this stack overflow question, Django - replacing built-in templatetag by custom tag for a whole site without {% load .. %}, points the way.
I believe I can provide a way for you to get the results you want, but there might be a better way for you to achieve the desired functionality if you can provide some context.
Anyway, you might do something like this in your view.py:
tags_list = [
Template('{% load my_tags %}{% ' + t.tag + ' %}').render(Context())
for t in tags_list
]

Django HTML snippet for a model

I am new to Django and I am trying to find out how can I associate a HTML snippet with a model.
My HTML snippet is just a div. I want to reuse that div (you can think of it like a thumbnail)
Situation is like this: In my main page I want to show x objects and in my search page I want to show more objects. But the representation is the same.
I can write in the mainpage.html and searchpage.html the desired way of transforming the model object into a div but I am not sure what is the best way to reuse that transform?
I can add a function to my model which returns a HTML div, then I can call it from mainpage and searchpage templates. But that will couple the model and representation, which I believe is not a very nice thing to do.
If I am not mistaken inclusion_tags are the way to go but in which file should I keep the function definition?
As you rightly say, you don't want html in your models, it's goes against the MVC pattern.
Models are for storing data. Views are for selecting the data to present whilst Templates are used for the actual presentation of data. (See this note on django's interpretation of MVC).
To answer your question, you need a template (or even a template tag), to represent your model, say model_block.html. Something like
{% if obj %}
<div id=obj.id>
{{ obj.name }}
</div>
{% endif %}
Then include your template in your mainpage.html like
{% for obj in object_list %}
{% include 'model_block.html' %}
{% endfor %}
You can do the same thing in your searchpage.html, passing a different object_list variable from your view.

Handlebars.js in Django templates

I need a javascript templating system and i think handlebars.js does an excellent job in this case.
I'm having syntax conflicts with handlebars templates inside a django template because django tries to render handlebars variables.
Is there a tag in django templates to stop rendering a block with curly braces?
Something like:
{{ django_context_varable }} #works
{{% raw %}}
<script id="restaurants-tpl" type="text/x-handlebars-template">
<ul>
{{#restaurants}} #not rendered by django, plain text
<li>{{name}}</li>
{{/restaurants}}
</ul>
</script>
{{% endraw %}}
Edit
Likely i found this. It works fine.
Update
Django 1.5 supports verbatim tag natively.
I use a custom template tag for another js templating system, here:
https://gist.github.com/629508
Use in template:
{% load mytags %}
{% verbatim %}
{{ This won't be touched by {% django's %} template system }}
{% endverbatim %}
Edit: This custom template tag is no longer necessary, as Django's template language now supports the {% verbatim %} template tag.
Is there a tag in django templates to stop rendering a block with curly braces?
OLD Answer for Django 1.0-1.4: No, though you could though you could put the block in a separate file and include it without rendering or use a different templating engine.
New Answer: The answer above was correct in August 2011 when the question was asked and answered. Starting in Django 1.5 (released Feb 2013, though alpha/beta versions in late 2012), they introduced the {% verbatim %} and {% endverbatim %} which will prevent the django template engine from processing the content in the block.
So for the question asked the following will work in django 1.5+ out of the box:
{{ django_context_varable }} #works
{% verbatim %}
<script id="restaurants-tpl" type="text/x-handlebars-template">
<ul>
{{#restaurants}} #not rendered by django, plain text
<li>{{name}}</li>
{{/restaurants}}
</ul>
</script>
{% endverbatim %}
The documentation on verbatim is here. Yes, this was noted by others earlier, but as this is the accepted answer I should list the easiest solution.
I wrote a very small django application : django-templatetag-handlebars exactly for that purpose.
{% load templatetag_handlebars %}
{% tplhandlebars "tpl-infos" %}
{{total}} {% trans "result(s)." %}
<p>{% trans "Min" %}: {{min}}</p>
<p>{% trans "Max" %}: {{max}}</p>
{% endtplhandlebars %}
Render your block as usual using Handlebars.js API :
var properties = {
total: 10,
min: 5,
max: 4
};
var template = Handlebars.compile($('#tpl-infos').html()),
rendered = template(properties);
I wrote it the day #chrisv published its package, with a KISS approach in mind. It is mainly based on Miguel Araujo's gist : https://gist.github.com/893408.
for a deeper integration between handlebars and Django (including optional on-the-fly precompilation) check out my project at
https://bitbucket.org/chrisv/django-handlebars/
It basically works like this:
create HB template under
appdirectory/hbtemplates/myapp/template.html
(just like Django template)
in your app, use
{% handlebars myapp %}
template tag and render template like so:
Handlebars.templates["myapp.template.html"]({context:"value"});
Compile your handlebars first!
From handlebars precompiling documentation:
In addition to reducing the download size, eliminating client-side compilation will significantly speed up boot time, as compilation is the most expensive part of Handlebars.
You can compile templates in your build environment using handlebars npm module, or integrate it with a build tool like gulp with gulp-handlebars.
After compiling, your handlebars templates can be served as static resources and bypass server side rendering altogether. Makes it easier on caching too :)
Typical usage would look like this:
<div id="restaurants-tpl">
Waiting for content...
</div>
<script src="{% static 'js/handlebars.runtime.js' %}"></script>
<script src="{% static 'js/templates.js' %}"></script>
<script>
// Let django render this as a json string
properties = {{ properties }};
// Use Handlebars compiled template imported above
rendered_html = Handlebars.templates["restaurants-tpl"](properties);
// Set element content
document.getElementById("restaurants-tpl").innerHTLM = rendered_html;
</script>
Django's templating system doesn't support escaping blocks at a time. It would be easy to work around were it not for the fact that when templates are processed the tokenizer doesn't keep exact information on what the tokens looked like before they got tokenized.
I have used the following work-around which is ugly, but (sort of) works. Use different tag delimiters in your templates and a django template tag that translates those back to what you actually want:
#register.tag(name="jstemplate")
def do_jstemplate(parser, token):
while self.tokens:
token = self.next_token()
if token.token_type == TOKEN_BLOCK and token.contents == endtag:
return
self.unclosed_block_tag([endtag])
nodelist = parser.parse( ('endjstemplate',) )
parser.delete_first_token()
s = token.split_contents()
tmpl_id = Variable( s[1] ) if (len(s) == 2 and s[1]) else ''
return JsTemplateNode( nodelist, tmpl_id )
class JsTemplateNode(template.Node):
def __init__(self, nodelist, tmpl_id=''):
self.tmpl_id = tmpl_id
self.nodelist = nodelist
def render(self, context):
content = self.nodelist.render(context)
return u'<script id="%s" type="text/x-handlebars-template">%s</script>' % (
self.tmpl_id.resolve(context),
re.sub( ur'{\$(.*?)\$}', u'{{\\1}}', content ), )
For bonus points you can leverage Django's templates within your templates ...
which will probably cook your brain trying to untangle later:
{% jstemplate "restaurants-tpl" %}
{$#restaurants$}
<div id="<$name$<" class="{$type$}">
<ul class="info">
{$#if info/price_range$}<li><em>{{ trans "Price Range" }}:</em> {$info/price_range$}</li>{$/if$}
{$#if info/awards$}<li><em>{{ trans "Awards" }}:</em> {$info/awards$}{$/if$}
</ul>
<div class="options">
<button>{% trans "Reservation" %}</button>
</div>
</div>
{$/restaurants$}
{% jstemplate %}
Actually I wrote a custom template filter which goes like this:
from django import template
register = template.Library()
def handlebars(value):
return '{{%s}}' % value
register.filter('handlebars', handlebars)
and use it in a template like so:
{{"this.is.a.handlebars.variable"|handlebars}}
It's the simplest thing I could think of. You just have to put your handlebars variable name in quotes. I regret I hadn't got this idea before I struggled with ssi. It works also with keywords:
{{"#each items"|handlebars}}
Why not use jinja2 instead? IMO, they're both elegant to use. Here's an excellent article about it: Using Jinja2 with Django