How to concatenate strings in django templates?

How to concatenate strings in django templates?

I want to concatenate a string in a Django template tag, like:
{% extend shop/shop_name/base.html %}

Here shop_name is my variable and I want to concatenate this with rest of path.
Suppose I have shop_name=example.com and I want result to extend shop/example.com/base.html.

Solutions/Answers:

Answer 1:

Use with:

{% with "shop/"|add:shop_name|add:"/base.html" as template %}
{% include template %}
{% endwith %}

Answer 2:

Don’t use add for strings, you should define a custom tag like this :

Create a file : <appname>\templatetags\<appname>_extras.py

from django import template

register = template.Library()

@register.filter
def addstr(arg1, arg2):
    """concatenate arg1 & arg2"""
    return str(arg1) + str(arg2)

and then use it as @Steven says

{% with "shop/"|addstr:shop_name|addstr:"/base.html" as template %}
    {% include template %}
{% endwith %}

Reason for avoiding add

According to the docs

This filter will first try to coerce both values to integers…
Strings that can be coerced to integers will be summed, not concatenated

If both variables happen to be integers, the result would be unexpected.

Answer 3:

I have changed the folder hierarchy

/shop/shop_name/base.html To /shop_name/shop/base.html

and then below would work.

{% extends shop_name|add:"/shop/base.html"%} 

Now its able to extend the base.html page.

Answer 4:

Refer to Concatenating Strings in Django Templates:

  1. For earlier versions of Django:

    {{ "Mary had a little"|stringformat:"s lamb." }}

“Mary had a little lamb.”

  1. Else:

    {{ "Mary had a little"|add:" lamb." }}

“Mary had a little lamb.”

Answer 5:

Have a look at the add filter.

Edit: You can chain filters, so you could do "shop/"|add:shop_name|add:"/base.html". But that won’t work because it is up to the template tag to evaluate filters in arguments, and extends doesn’t.

I guess you can’t do this within templates.

Answer 6:

From the docs:

This tag can be used in two ways:

  • {% extends "base.html" %} (with quotes) uses the literal value “base.html” as the name of the parent template to extend.
  • {% extends variable %} uses the value of variable. If the variable evaluates to a string, Django will use that string as the name of the parent template. If the variable evaluates to a Template object, Django will use that object as the parent template.

So seems like you can’t use a filter to manipulate the argument. In the calling view you have to either instantiate the ancestor template or create an string variable with the correct path and pass it with the context.

Answer 7:

@error’s answer is fundamentally right, you should be using a template tag for this. However, I prefer a slightly more generic template tag that I can use to perform any kind of operations similar to this:

from django import template
register = template.Library()


@register.tag(name='captureas')
def do_captureas(parser, token):
    """
    Capture content for re-use throughout a template.
    particularly handy for use within social meta fields 
    that are virtually identical. 
    """
    try:
        tag_name, args = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError("'captureas' node requires a variable name.")
    nodelist = parser.parse(('endcaptureas',))
    parser.delete_first_token()
    return CaptureasNode(nodelist, args)


class CaptureasNode(template.Node):
    def __init__(self, nodelist, varname):
        self.nodelist = nodelist
        self.varname = varname

    def render(self, context):
        output = self.nodelist.render(context)
        context[self.varname] = output
        return ''

and then you can use it like this in your template:

{% captureas template %}shop/{{ shop_name }}/base.html{% endcaptureas %}
{% include template %}

As the comment mentions, this template tag is particularly useful for information that is repeatable throughout a template but requires logic and other things that will bung up your templates, or in instances where you want to re-use data passed between templates through blocks:

{% captureas meta_title %}{% spaceless %}{% block meta_title %}
    {% if self.title %}{{ self.title }}{% endif %}
    {% endblock %}{% endspaceless %} - DEFAULT WEBSITE NAME
{% endcaptureas %}

and then:

<title>{{ meta_title }}</title>
<meta property="og:title" content="{{ meta_title }}" />
<meta itemprop="name" content="{{ meta_title }}">
<meta name="twitter:title" content="{{ meta_title }}">

Credit for the captureas tag is due here: https://www.djangosnippets.org/snippets/545/

Answer 8:

I found working with the {% with %} tag to be quite a hassle. Instead I created the following template tag, which should work on strings and integers.

from django import template

register = template.Library()


@register.filter
def concat_string(value_1, value_2):
    return str(value_1) + str(value_2)

Then load the template tag in your template at the top using the following:

{% load concat_string %}

You can then use it the following way:

<a href="{{ SOME_DETAIL_URL|concat_string:object.pk }}" target="_blank">123</a>

I personally found this to be a lot cleaner to work with.

Answer 9:

You can’t do variable manipulation in django templates.
You have two options, either write your own template tag or do this in view,

Answer 10:

extends has no facility for this. Either put the entire template path in a context variable and use that, or copy the exist template tag and modify it appropriately.

References