Simple Django question: how to allow html as output from a variable? - django

this template variable {{object.video.description}} is outputing this text:
Welcome to Saint Francis Academy in the heart of Washington.
How can I get the link to show as an actual link instead of being replaced with html entities. I tried filtering it as safe but no luck: {{object.video.description|safe}}

Can you go to the django shell and see what text is recorded in object.video.description?
How/where does video.description get defined as an html string (what I'm guessing is that a < is already be escaped into < at that point and hence safe won't help). Marking as safe prevents django from converting < to < right before rendering in the template; but won't convert a string containing < into a <.
If the string is originally saved with <s and &gts you can convert them to < and > by a simple python replacement somewhere in your string processing. E.g., in your view do something like:
htmlCodes = (('&', '&'),
('<', '<'),
('>', '>'),
('"', '"'),
("'", '''),)
def unescape(some_html_str):
for c, html_code in htmlCodes:
some_html_str = some_html_str.replace(html_code, c)
return some_html_str
and then remember to unescape your string in your view before putting it in the context (and still remember to mark it safe).
See How do I perform HTML decoding/encoding using Python/Django?
Also it may be better/easier for you to use mark_safe (from django.utils.safestring import mark_safe) in your views to make sure only safe strings are marked safe rather than have your template always render something safe.

{% load markup %}
{{ object.video.description|markdown }}

Related

Detect URLs and #tags in Django CharField and add style to it

For now on I have in my template a paragraph like this <p class="...">{{ post.content }}</p> and if this Post's content contains a link or #hashtag it is rendered as a normal text with the rest of the post. How can I customize it? For example change text-color and add tag around it?
As I said in comment, you can use custom tag filter to wrap your content, and use Regular Expression to generate links and hashtags
Create your tags file, and name it as you want:
tag_filter_name.py
If you're not familiar with custom tag filter creation, you can learn more about it in the Official Documentation
from django import template
import re
register = template.Library()
def generate_link(link):
return '<a class="link" href="{}">{}</a>'.format(link, link)
def generate_hashtag_link(tag):
# Free to configuree the URL the way adapted your project
url = "/tags/{}/".format(tag)
return '<a class="hashtag" href="{}">#{}</a>'.format(url, tag)
And then, you create the function what will be used as tag filter
#register.filter
def render_content(obj):
text = re.sub(r"#(\w+)", lambda m: generate_hashtag_link(m.group(1)),obj)
return re.sub(r"(?P<url>https?://[^\s]+)", lambda m: generate_link(m.group(1)),text)
If you want Django to mark it as safe content, you can do the following:
from django.utils.safestring import mark_safe # import function
''' function codes here '''
return mark_safe(re.sub(r"(?Phttps?://[^\s]+)",
lambda m: generate_link(m.group(1)),text))
And finally, to use it in your template, don't forget to load it
{% load tag_filter_name %}
{{ post.content|render_content }}
Best way: custom tag filters here is the docs URL
https://docs.djangoproject.com/en/2.2/ref/templates/builtins/
Good way: If you know JS create a function that handles the formatting on the front end
in HTML:
onload="myfunction("{{post.content}}")"
in JS sort for the string containing the # wrap it in a span or other element and style away. then replace the inner HTML with your new formatted piece. This will save rendering time on the server and also frees you up from having to loop thru the list of posts in the view
Ok way: not preferred but if you hate js and only want to work in python (understandable). You need to loop through the list of posts separate out the items of the post format them the way you like with inline style. then add them to a new object that you will append to the end of a new list of posts that you will then pass thru to context. This is a real pain please don't do this if you can help it at all.
the tag filters are awsome take advantage but if they won't work for your use case I would highly advise using vanilla JS

Django HTML truncation

I'm using the build-in truncatewords_html filter of Django and it adds "..." in the end, instead, I want to replace this with a link "See More".
How can I achieve this?
It would be best to write your own filter. You could take the source code for truncatewords_html and use it as a template for your filter. It should take a few changes to get what you want, and then you will just need to register your template and make sure you load it on the page you want to use it on and you should be good.
See this page for more info
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
https://code.djangoproject.com/browser/django/trunk/django/template/defaultfilters.py#L288
You should be able to copy the method and just change the Code to this.
return Truncator(value).words(length, html=True, truncate=' see more')
You want to make 'see more' a link, that will take more code. I would change the filter to accept another param which is the link for 'see more'.
Then instead of just having 'see more' passed to Truncator you would pass the HTML link.
If you wanted to pass a custom link, that could be done like this.
Define your custom filter:
from django import template
from django.utils.safestring import mark_safe
from django.utils.text import truncate_html_words
register = template.Library()
#register.filter
def truncatewords_html_with_link(value, arg):
"""
Truncates HTML after a certain number of words and concatenates a link
Argument: String - Number of words to truncate after and the link,
separated by a comma
"""
arg_list = arg.split(',')
try:
length = int(arg_list[0])
except ValueError:
return value
return mark_safe(truncate_html_words(value, length, arg_list[1]))
Call it from your template:
{{ text|truncatewords_html_with_link:"5, <a class=\"read-more\" href=\"/your_url/\">Read More</a>" }}
The relevant code in Django 1.8 reads:
truncate = pgettext(
'String to return when truncating text',
'%(truncated_text)s...')
If you are using LOCALE and translation files, place the following into your *.po files:
msgid "String to return when truncating text"
msgstr "Short version: %(truncated_text)s <a class='see-more-link'>see more</a>"
Though, depending on what should happen when you click on the link adding it that way might not be very helpful. You could use another placeholder for it, but then you would have to make sure to replace the placeholder whereever this message string is used.

How do you mark strings as "Safe" in a view (or the template) in Jinja2?

Typically when you want to mark string output as safe in Jinja2 you do something like this:
{{ output_string|safe() }}
However, what if output_string is always safe? I don't want to repeat myself every time by using the safe filter.
I have a custom filter called "emailize" that preps urls for output in an email. The ampersands always seem to become escaped. Is there a way in my custom filter to mark the output as safe?
Check SafeString, like for example:
from django.utils.safestring import SafeString
...
return context.update({
'html_string': SafeString(html_string),
})
Use the Markup class:
class jinja2.Markup([string])
Marks a string as being safe for inclusion in HTML/XML output without needing to be escaped.

Django view returns string which is not represented correctly in html template

A view I am using returns a string to a html template. The string which is returned contains special characters which are not represented correctly in the template. To put it simply a string which is returned from the view might look like this:
test = "\"Foo bar\""
return render_to_response('index.html', {'test': test})
And is displayed in the html template like this:
& quot;Foo bar& quot;
How can I fix the encoding so it is displayed correctly?
Use the safe filter when printing:
{{ test|safe }}
Or, do this in the view:
from django.utils.safestring import mark_safe
test = mark_safe("\"Foo bar\"")
Please note that by doing this you are telling Django that the contents of the string are safe and do not need HTML escaping. If you are planning to put anything whatsoever that could come from the user this would then leave you vulnerable to XSS attacks, so use with caution.
Your best bet is to consult the Django documentation, which explains this in detail:
https://docs.djangoproject.com/en/2.0/ref/templates/language/#automatic-html-escaping

Is is possible to html encode output in AppEngine templates?

So, I'm passing an object with a "content" property that contains html.
<div>{{ myobject.content }}</div>
I want to be able to output the content so that the characters are rendered as the html characters.
The contents of "conent" might be: <p>Hello</p>
I want this to be sent to the browser as: &amplt;p&ampgt;Hello&amplt;/p>
Is there something I can put in my template to do this automatically?
Yes, {{ myobject.content | escape }} should help (assuming you mean Django templates -- there's no specific "App Engine" templating system, GAE apps often use the Django templating system); you may need to repeat the | escape part if you want two levels of escaping (as appears to be the case in some but not all of the example you supply).
This is Django's django.utils.html.escape function:
def escape(html):
"""Returns the given HTML with ampersands, quotes and carets encoded."""
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '&l
t;').replace('>', '>').replace('"', '"').replace("'", '''))
Also, see here.