How to repeat a "block" in a django template - django

I want to use the same {% block %} twice in the same django template. I want this block to appear more than once in my base template:
# base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
<h1>{% block title %}My Cool Website{% endblock %}</h1>
</body>
</html>
And then extend it:
# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}
# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}
I will get an exception, as Django wants the block to appear only once:
TemplateSyntaxError at /
'block' tag with name 'title' appears
more than once
A quick and dirty solution would be duplicating the block title into title1 and title2:
# blog.html
{% extends 'base.html' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}
But this is a violation of the DRY principle. It would be very difficult as I have a lot of inheriting templates, and also because I don't wanna go to hell ;-)
Is there any trick or work-around to this problem? How can I repeat the same block in my template, without duplicating all the code?

Use the Django template macros plugin:
https://gist.github.com/1715202 (django >= 1.4)
or
http://www.djangosnippets.org/snippets/363/ (django < 1.4)
django >= 1.4
# base.html
{% kwacro title %}
{% block title %}My Cool Website{% endblock %}
{% endkwacro %}
<html>
<head>
<title>{% usekwacro title %}</title>
</head>
<body>
<h1>{% usekwacro title %}</h1>
</body>
</html>
and
# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
django < 1.4
# base.html
{% macro title %}
{% block title %}My Cool Website{% endblock %}
{% endmacro %}
<html>
<head>
<title>{% usemacro title %}</title>
</head>
<body>
<h1>{% usemacro title %}</h1>
</body>
</html>
and
# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

I think that use of the context processor is in this case an overkill. You can easily do this:
#base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
and then:
# blog.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %}My Blog{% endblock %}</h1>
Lorem ipsum here...
{% endblock %}
and so on... Looks like DRY-compatible.

You probably don't actually want to use a block but rather to just use a variable:
# base.html
<html>
<head>
<title>{{ title|default:"My Cool Website" }}</title>
</head>
<body>
<h1>{{ title|default:"My Cool Website" }}</h1>
</body>
</html>
You then set the title through the context.

Here's a way I discovered when trying to do the same thing myself:
# base_helper.html
<html>
<head>
<title>{% block _title1 %}{% endblock %}</title>
</head>
<body>
<h1>{% block _title2 %}{% endblock %}</h1>
</body>
</html>
# base.html
{% extends "base_helper.html" %}
# Copy title into _title1 & _title2, using "My Cool Website" as a default.
{% block _title1 %}{% block _title2 %}{% block title %}My Cool Website{% endblock %}{% endblock %}{% endblock %}
Requires an extra file unfortunately, but doesn't require you to pass the title from the view.

you can use {% include subtemplate.html %} more than once. it's not the same as blocks, but does the trick.

There are some discussion here:
http://code.djangoproject.com/ticket/4529
Obviously django core team reject this ticket because they think this is not a common used scenario, however I disagree.
repeat block is simple and clean implementation for this:
https://github.com/SmileyChris/django-repeatblock
template macros is another one, however the author mentioned it's not carefully tested:
http://www.djangosnippets.org/snippets/363/
I used repeatblock.

As an update for anyone coming across this, I've taken the snippet mentioned above and turned it into a template tag library, django-macros, makes the macros more powerful and also implements a repeated block pattern explicitly: django-macros.

Here is a lightweight solution similar to the above do_set and do_get template tag answer. Django allows you to pass the entire template context into a tag which can allow you to define a global variable.
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<title>{{ title }}</title>
{% endblock %}
</head>
<body>
<h1>{{ title }}</h1>
</body>
</html>
page.html:
{% extends "base.html" %}
{% block head %}
{% define 'title' 'Homepage | title' %}
{{ block.super }}
{% endblock %}
custom tag (got the idea here: https://stackoverflow.com/a/33564990/2747924):
#register.simple_tag(takes_context=True)
def define(context, key, value):
context.dicts[0][key] = value
return ''
Also don't forget to {% load %} your custom tags or add them to the template options builtins list so you don't have to load them in every template. The only limitation to this approach is the {% define %} has to be called from within a block tag because child templates only render block tags that match the parent tags. Not sure if there is a way around that. Also make sure the define call comes before you try to use it obviously.

Building on Van Gale's suggestion, you could create get and set tags by adding the following to your templatetags.py file:
register = template.Library()
Stateful = {}
def do_set(parser, token):
_, key = token.split_contents()
nodelist = parser.parse(('endset',))
parser.delete_first_token() # from the example -- why?
return SetStatefulNode(key,nodelist)
class SetStatefulNode(template.Node):
def __init__(self, key, nodes):
Stateful[key] = nodes
def render(self, context):
return ''
register.tag('set', do_set)
def do_get(parser, token):
tag_name, key = token.split_contents()
return GetStatefulNode(key)
class GetStatefulNode(template.Node):
def __init__(self, key):
self.key = key
def render(self, context):
return ''.join( [x.render(context) for x in Stateful[self.key]] )
register.tag('get', do_get)
Then set values in one template via {% set foo %}put data here{% endset %} and get them via {% get foo %} in another.

I too have come across the same need for a repeated {% block %} in my template files. The issue is that I want a Django {% block %} to be used in either case of a Django conditional, and I want the {% block %} to be over-writable by subsequent files that may extend the current file. (So in this case, what I want is definitely more of a block than a variable because I'm not technically re-using it, it just appears on either end of a conditional.
The Problem:
The following Django template code will result in a Template Syntax Error, but I think it's a valid "want" to have a defined {% block %} re-used in a conditional (IE, why is the Django parser validating syntax on BOTH ends of a conditional, shouldn't it only validate the TRUTHY condition?)
# This example shows a {{ DEBUG }} conditional that loads
# Uncompressed JavaScript files if TRUE
# and loads Asynchronous minified JavaScript files if FALSE.
# BASE.html
{% if DEBUG %}
<script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
<script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
<script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
<script type="text/javascript">
{% block page_js %}
var page = new $site.Page();
{% endblock page_js %}
</script>
{% else %}
<script type="text/javascript">
// load in the PRODUCTION VERSION of the site
// minified and asynchronosly loaded
yepnope([
{
load : '{MEDIA_URL}}js/flatfiles.min.js',
wait : true,
complete : function() {
{% block page_js %} // NOTE THE PAGE_JS BLOCK
var page = new $site.Page();
{% endblock page_js %}
}
}
)];
</script>
{% endif %}
# ABOUT.html
{% extends 'pages/base.html' %}
{% block page_js %}
var page = new $site.Page.About();
{% endblock page_js %}
The Solution:
You can use an {% include %} to conditionally insert a {% block %} more than once. This worked for me because the Django syntax checker includes only the TRUTHY {% include %}. See the result below:
# partials/page.js
{% block page_js %}
var page = new $site.Page();
{% endblock %}
# base.html
{% if DEBUG %}
<script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
<script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
<script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
<script type="text/javascript">
{% include 'partials/page_js.html' %}
</script>
{% else %}
<script type="text/javascript">
yepnope([
{
load : '{MEDIA_URL}}js/flatfiles.min.js',
wait : true,
complete : function() {
{% include 'partials/page_js.html' %}
}
}
)];
</script>
{% endif %}

I use this answer to keep things dry.
{% extends "base.html" %}
{% with "Entry Title" as title %}
{% block title %}{{ title }}{% endblock %}
{% block h1 %}{{ title }}{% endblock %}
{% endwith %}

There are two easy solutions for this.
The easiest is to put your title into a context variable. You would set the context variable in your view.
If you are using something like generic views and don't have a views.py for pictures, cats, etc. then you can go the way of a custom template tag that sets a variable in the context.
Going this route would enable you to do something like:
{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...
Then in your base.html:
...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>

The selected answer alludes to an easy workaround to wrap one tag inside another in the child template to give them both the same value. I use this for social images like so.
Child template:
{% extends 'base.html' %}
...
{% block meta_image %}
{% block meta_image_secure %}
{% if object.cover_pic %}
{{ object.cover_pic.url }}
{% else %}
https://live-static.welovemicro.com/static/img/device-dark.png
{% endif %}
{% endblock %}
{% endblock %}
...
Then in the parent base.html:
...
<meta property="og:image" itemprop="image" content="{% block meta_image %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
<meta property="og:image:secure_url" itemprop="image" content="{% block meta_image_secure %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
...

I would suggest using the django-macros library.
To install:
pip install django-macros
And then add the 'macros' app in your INSTALLED_APPS list in settings.py.
After that, load the macros custom tags in your base template and use them like that:
{# base.html #}
{% load macros %}
{% macro title %}
{% block title %} My Cool Website {% endblock %}
{% endmacro %}
<html>
<head>
<title> {% use_macro title %} </title>
</head>
<body>
<h1> {% use_macro title %} </h1>
</body>
</html>

In twig you can make this like:
# base.html
<html>
<head>
<title>{{ block('title') }}</title>
</head>
<body>
<h1>{{ block('title') }}</h1>
</body>
</html>
# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}
# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

Related

Django How can I repeat a block within a template

My Django base template looks like this:
#base.html
<title>{% block title %} My Default Title {% endblock title %}</title>
<meta property="og:title" content="{% block og_title %} My Default Title {% endblock og_title %}">
Then if I'd like to override the default title for specific page:
#page.html
{% extends "base.html" %}
{% block title %} My page Title {% endblock title %}
{% block og_title %} My page Title {% endblock _ogtitle %}
How can I set the base.html block title once (like a variable) and use across my document? Then, if I need, I override the title once in the page.html and it gets populated across?
You can work with a variable in the base template:
<title>{{ my_title|default:"my page title" }}</title>
<meta property="og:title" content="{{ my_title|default:"my page title" }}">
In your view, you then pass a value for my_title:
from django.shortcuts import render
def some_view(request):
# …
return render(request, 'some_template.html', {'my_title': 'some_title'})
Answering my own question:
There is no good way to do that. There are a few workarounds mentioned here:
How to repeat a "block" in a django template
(I saw this question in advance. But wasn't sure if anything changed)
This is the workaround I chose:
#base.html
<title>{% block title %} My Default Title {% endblock title %}</title>
<meta property="og:title" content="{% block og_title %} My Default Title {% endblock og_title %}">
#page.html
{% extends "base.html" %}
{% block title %} {% block og_title %} My page Title {% endblock og_title %}{% endblock title %}

Django: Media for Views? [duplicate]

I am updating old django code from method-based views to class-based views.
I know how to include media (css/js) in forms via the Media class
How can I use the media class if my class based view does not contain any forms?
CSS/JS are usually managed in the template itself and not in the view. See https://docs.djangoproject.com/en/1.10/howto/static-files/
For example, use base.html:
<!DOCTYPE html>
<html>
<head>
<title>
{% block page_title %}{{ page_title }}{% endblock %}
</title>
{% block css %}
{% endblock %}
</head>
<body>
{% block main %}
{% endblock %}
{% block scripts %}
{% endblock %}
</body>
</html>
and extend it with my_page.html:
{% extends "base.html" %}
{% load staticfiles %}
{% block page_title %}
Hello!
{% endblock %}
{% block css %}
<link href="{% static "page.css" %}" rel="stylesheet"/>
{% endblock %}
{% block main %}
Yo!
{% endblock %}
{% block scripts %}
<script src="{% static 'my_scripts.js' %}"></script>
{% endblock %}
Django Sekizai is meant for this:
Here is the example from their documentation:
{% load sekizai_tags %}
<html>
<head>
{% render_block "css" %}
</head>
<body>
Your content comes here.
Maybe you want to throw in some css:
{% addtoblock "css" %}
<link href="/media/css/stylesheet.css" media="screen" rel="stylesheet" type="text/css" />
{% endaddtoblock %}
Some more content here.
{% addtoblock "js" %}
<script type="text/javascript">
alert("Hello django-sekizai");
</script>
{% endaddtoblock %}
And even more content.
{% render_block "js" %}
</body>
</html>
This example shows everything in one template, but - of course - you can split that into multiple templates either by inheritance or includes and use the addtoblock directives in any of the partial templates.
A more complex, real life example is also in their documentation.
Here is a small mixin class that can help you add Media to views based on CreateView, UpdateView, and DeleteView:
class InjectFormMediaMixin(object):
def get_form_class(self):
form_class = super(InjectFormMediaMixin, self).get_form_class()
if hasattr(self, 'Media') and not hasattr(form_class, 'Media'):
form_class.Media = self.Media
return form_class
Example:
class CreateFooView(InjectFormMediaMixin, CreateView):
model = models.Foo
fields = (
'name',
)
class Media:
css = {
'all': ('pretty.css',)
}
js = ('animations.js', 'actions.js')

Django 'block' tag with name 'title' appears more than once

I'm trying to implement logic for my website with a title. But have this error - Django 'block' tag with name 'title' appears more than once. How do i fix this?
base.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %} Main {% endblock %}</title>
<meta charset="utf-8">
In my template product_list
{% extends 'shop/application.html' %}
{% if category %}
{% block title %} Product name of category {% endblock %}
{% elif subcategory %}
{% block title %} Product name of subcategory {% endblock %}
{% endif %}
...
How to implement it? Thanks for help!
Template blocks do not look what is outside of that block. If you override the content of a {% block %} in an {% if condition %} ... {% endif %}, and the condition is False, you will still override the block.
You should "inject" the condition in the {% block ... %} override, like:
{% block title %}
{% if category %}A{%else %}B{% endif %}
{% endblock %}

extended template working but main template is not working django

views.py
def base(request):
return render(request,"base.html",{'':''})
def index(request):
return render(request,"index.html",{'':''})
base.html
<html>
<head>ppppppppp</head>
<body>
<h1>this is base template</h1>
</body>
</html>
index.html
{% extends "base.html" %}
{% block content %}
<body>
<h1>
Welcome to my app
</h1>
</body>`
{% endblock content %}
Here the issue is django is not at all recognising index.html only displaying extended template.
The correct flow here is to create really :) base template:
base.html
<html>
<head>ppppppppp</head>
<body>
{% block content %} {% endblock %}
</body>
</html>
And in your child templates you can override {% block content %} but leaving <head> etc common for all templates:
index.html
{% extends "base.html" %}
{% block content %}
<h1>Welcome to my app</h1>
{% endblock %}
Also you should not write tag like {% endblock BLOCKNAME %}, just {% endblock %}

Strip HTML from Django template block?

In my Django base.html template I have a title block:
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
In a page template I use the text from its <h1> page title in that block, so it also appears in the <title> tag, e.g.:
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %}Hello world!{% endblock %}</h1>
{% endblock %}
That all works fine. But if I want to use HTML tags within the page's <h1> like this...
{% extends 'base.html' %}
{% block content %}
{% block title %}<b>Hello</b> world!{% endblock %}
{% endblock %}
...those tags will also appear in the <title>, which isn't allowed.
Is there a way around this other than having two versions of the title: one within <h1>, and one HTML-free version within a {% block title %}? I don't think there's a way to strip HTML from a block?
You could use cycle like so:
{% extends 'base.html' %}
{% block content %}
<h1>
{% block title %}
{% cycle '' '<b>' %}Hello world!{% cycle '' '</b>' %}
{% endblock %}
</h1>
{% endblock %}
Hope this helps