[Solved]-Remove line breaks from Django template

5๐Ÿ‘

โœ…

The spaceless tag removes spaces between html tags, itโ€™s not possible to get it to remove spaces within tags, as you are trying to do.

You can prevent newlines by putting the if/else clause on one line, but as you say, that makes it hard to read. This suggests to me that you are trying to do too much work in the Django templates, and that the urls should be constructed else where.

The simplest option is to construct a list of (language_code, url)ย tags in the view, and loop through these in the template.

{% for lang_code, url in language_urls %}
    <link hreflang={{ lang_code }} href="{{ url }}">
{% endfor %}

If itโ€™s not convenient to do this in the view another couple of options are:

  • a template context processor (good if you use the same urls in every view
  • a custom template tag/filter (good if some_path and other_path are dynamic)
๐Ÿ‘คAlasdair

14๐Ÿ‘

I needed the same, because Iโ€™m using a template to generate an email subject line (non-HTML, so {% spaceless %} is useless). In my case, the template had taken on lots of different cases, and everything had to be written in a single line over about 500 characters.

So in the spirit of {% spaceless %}, I wrote my own {% linebreakless %}:

import six
from django import template
from django.template.base import Node
from django.utils.functional import allow_lazy


register = template.Library()


@register.tag
def linebreakless(parser, token):
    nodelist = parser.parse(('endlinebreakless',))
    parser.delete_first_token()
    return LinebreaklessNode(nodelist)


class LinebreaklessNode(Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        strip_line_breaks = allow_lazy(lambda x: x.replace('\n', ''), six.text_type)
        return strip_line_breaks(self.nodelist.render(context).strip())

Please note that it (intentionally!) preserves all whitespace except line breaks.

An example usage for an email template would look like this, assuming the above is loaded in a template tag module called linebreakless.py:

{% load linebreakless %}{% linebreakless %}

{# Email subjects are (encoded) text, not HTML, so we don't need any autoescaping! #}
{% autoescape off %}

New Notification
 โ€ข
{% if flag %}
 about this
{% else %}
 about that
{% endif %}
!

{% endautoescape %}
{% endlinebreakless %}

Note that the {% linebreakless %} tag has to be on the first line (and after the {% load %} directives) to prevent any line breaks in the generated file.

1๐Ÿ‘

An alternative is to use a Jinja2 template, which Django supports since 1.8. From Jinja2โ€™s documentation on whitespace control:

If you add a minus sign (-) to the start or end of a block (e.g. a For tag), a comment, or a variable expression, the whitespaces before or after that block will be removed:

{% for item in seq -%}
    {{ item }}
{%- endfor %}

Converting a template from Django to Jinja2 is not entirely straightforward. It may be worth it though if youโ€™re using the template to build a text file rather than an HTML file.

1๐Ÿ‘

If you just want to remove the nextline which is coming due to < br > tag then just use โ€“

{{value|striptags}}

This will remove the tags and output will be simple and style (also new line) free. Hope it works ๐Ÿ™‚

๐Ÿ‘คSourav Purkait

1๐Ÿ‘

I needed it and updated @Henrik โ€˜s answer to work with keep_lazy

 @register.tag
 def linebreakless(parser, token):
     nodelist = parser.parse(('endlinebreakless',))
     parser.delete_first_token()
     return LinebreaklessNode(nodelist)


 class LinebreaklessNode(Node):
     def __init__(self, nodelist):
         self.nodelist = nodelist

     def render(self, context):
         strip_line_breaks = keep_lazy(six.text_type)(lambda x: x.replace('\n\n', '\n'))

         return strip_line_breaks(self.nodelist.render(context).strip())
๐Ÿ‘คeugene

0๐Ÿ‘

Just wanting to throw out my own solution here, following much of what @henrik-heimbuerger provided above. This is a solution that effectively copy/pastes Djangoโ€™s spaceless tag (as of Django 3.2) and then adapts it slightly. This way, we benefit from it being "lazy" (@keep_lazy_text). This function will also remove all whitespace and handles trailing spaces appropriately for HTML.

Iโ€™m providing here in entirety to allow others to easily use this with a copy/paste themselves ๐Ÿ˜‰

import re

from django import template
from django.template import Node
from django.utils.functional import keep_lazy_text

register = template.Library()

@register.tag
def whitespaceless(parser, token):
    """
    Remove whitespace within HTML tags,
    including tab, newline and extra space
    characters.

    Example usage::

        {% whitespaceless %}
            <p class="  test
                        test2
                        test3  ">
                <a href="foo/">Foo</a>
            </p>
        {% endwhitespaceless %}

    This example returns this HTML::

        <p class="test test2 test3"><a href="foo/">Foo</a></p>

    This affects all text within the
    `whitespaceless` command without prejudice.
    Use with caution.
    """
    nodelist = parser.parse(('endwhitespaceless',))
    parser.delete_first_token()
    return WhitespacelessNode(nodelist)


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

    def render(self, context):
        return strip_whitespace(self.nodelist.render(context).strip())


@keep_lazy_text
def strip_whitespace(value):
    """
    Return the given HTML with any newlines, 
    duplicate whitespace, or trailing spaces 
    are removed .
    """
    # Process duplicate whitespace occurrences or 
    # *any* newline occurrences and reduce to a single space
    value = re.sub(r'\s{2,}|[\n]+', ' ', str(value))
    # After processing all of the above,
    # any trailing spaces should also be removed
    # Trailing space examples:
    #   - <div >                    Matched by: \s(?=[<>"])
    #   - < div>                    Matched by: (?<=[<>])\s
    #   - <div class="block ">      Matched by: \s(?=[<>"])
    #   - <div class=" block">      Matched by: (?<==\")\s
    #   - <span> text               Matched by: (?<=[<>])\s
    #   - text </span>              Matched by: \s(?=[<>"])
    value = re.sub(r'\s(?=[<>"])|(?<==\")\s|(?<=[<>])\s', '', str(value))
    return value
๐Ÿ‘คWill Gordon

Leave a comment