Django templates - split string to array
Asked Answered
J

5

55

I have a model field, which stores a list of URLs (yeah, I know, that's wrong way) as url1\nurl2\nurl3<...>. I need to split the field into an array in my template, so I created the custom filter:

@register.filter(name='split')
def split(value, arg):
    return value.split(arg)

I use it this way:

{% with game.screenshots|split:"\n" as screens %}
        {% for screen in screens %}
            {{ screen }}<br>
        {% endfor %}
    {% endwith %}

but as I can see, split doesn't want to work: I get output like url1 url2 url3 (with linebreaks if I look at the source). Why?

Job answered 29/11, 2011 at 20:50 Comment(0)
C
107

Django intentionally leaves out many types of templatetags to discourage you from doing too much processing in the template. (Unfortunately, people usually just add these types of templatetags themselves.)

This is a perfect example of something that should be in your model not your template.

class Game(models.Model):
    ...
    def screenshots_as_list(self):
        return self.screenshots.split('\n')

Then, in your template, you just do:

{% for screen in game.screenshots_as_list %}
    {{ screen }}<br>
{% endfor %}

Much more clear and much easier to work with.

Cerallua answered 29/11, 2011 at 22:54 Comment(3)
Keep in mind, though, that excessively fat models can become a pile of unmaintainable stuff. This specific case is valid—you'd be converting some of your serialized data to Python, which makes sense to do on the model layer. (You can even make that method a computed property.) However, if you want to put a method on your model just so that you can call it from your template, think twice—maybe it'd be OK to do that kind of processing in your view(s) :)Mensa
better than template tags if content is from models.ModelGroceries
This answer is an excellent suggestion. Thank you! I need a further suggestion: since such a method is quite similar in functionality to a "getter" function, would you much prefer this method to be defined @property or keep it as a method?Etz
A
16

Functionality already exists with linkebreaksbr:

{{ value|linebreaksbr }}

https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#linebreaksbr

Agency answered 29/11, 2011 at 20:53 Comment(1)
No, I don't need simply to format them to HTML, I need definitly split it to the list to work with it.Job
J
8

Hm, I have partly solved this problem. I changed my filter to:

@register.filter(name='split')
def split(value, arg):
    return value.split('\n')

Why it didn't work with the original code?

Job answered 29/11, 2011 at 20:52 Comment(2)
Maybe you needed to escape \n?Egotism
if you pass "\n" from the template, this is passed as plain string, and the special meaning of the \n as a linebreak symbol is lost.Ssr
A
1

I wanted to split a list of words to get a word count, and it turns out there is a filter for that:

{{ value|wordcount }}

https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#wordcount

Agraffe answered 30/11, 2017 at 11:21 Comment(0)
D
1

Apart from whether your original solution was the right approach, I guess the original code did not work because the meaning of the \n is not the same in Python code as it is in HTML: In Python code it means the escaped newline character, in HTML it is just the two separate characters \ and n. So passing as input parameter \n from the HTML template to the Python code is equivalent to splitting on the Python string \\n: a literal \ followed by a n.

Doublethink answered 30/5, 2018 at 11:0 Comment(1)
actually, this is the answer that really answers the question (why is the OPs templatefilter not working?)! though, there is no solution to the problem.Ssr

© 2022 - 2024 — McMap. All rights reserved.