Reputation: 2881
I have a loop in a template like this
{% spaceless %}
{% for lang_code, lang_name in LANGUAGES %}
<link hreflang={{ lang_code }} href="http://example.com/
{% if lang_code|length > 2 %}
{{ some_path }}
{% else %}
{{ other_path }}
{% endif %}
">
{% endfor %}
{% endspaceless %}
Which produces something like this
<link hreflang="en-gb" href="http://example.com/
some_path/
"><link hreflang="de" ...>
Is it possible to re-write this code so that the whole tag is written in one line like so?
<link hreflang="en-gb" href="http://example.com/some_path/">
<link hreflang="de" href="http://example.com/other_path/">
...
P.S. This can be achieved if the if/else clause is stretched over one line, but then the line becomes unreadable.
Upvotes: 13
Views: 15261
Reputation: 3573
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
Upvotes: 1
Reputation: 41665
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())
Upvotes: 1
Reputation: 344
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 :)
Upvotes: 1
Reputation: 10183
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.
Upvotes: 15
Reputation: 6939
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.
Upvotes: 2
Reputation: 308779
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:
some_path
and other_path
are dynamic) Upvotes: 5