Django HTML truncation - django

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.

Related

Django: a custom template tag to convert links inside of a TextField and change the hyperlink text

The scenario is that there are some dynamic texts on some templates, that will contain hyperlinks.
For this, I have a SiteDataKeyValue model, in which the dynamic texts for different parts of the template are inputted. This is the model:
class SiteDataKeyValue(models.Model):
key = models.CharField(
max_length=200, verbose_name="نام متن مورد نظر", unique=True
)
value = models.TextField(verbose_name="متن")
def __str__(self):
return self.key
A solution that I've found already, is Django urlize template tag. As mentioned in the docs, this tag converts texts like https://www.google.com to www.google.com, which is nice but not what I'd like to achieve. I want to be able to change the hyperlink text, so the output would be something like: Click Here!.
I searched for a bit, came across modules like bleach, which is a fine module, but I couldn't find the answer I was looking for (I skimmed through the docs and there was nothing about the hyperlink text).
Also I saw a comment somewhere telling that this could be achieved by writing a custom Django template tag, but although I tried to do this regarding the custom template filters docs, I didn't have a clue to how to achieve this.
I'm not asking for the code, although it would be really appreciated if you provide instructions for writing this custom template tag, or better, if you could point me to something like this that is already out there.
First of all you can extend urlize tag like the answer in this
or you can change the main code which you can find it in django.utils.html and override its url variable to change it.
But I think the best method is extending the urlize tag
like this:
{% text | urlize | change_a_text_filter:{{ dome_new_a_text }} %}
then you can scrape the text and use regex to find >sample-text</a> then you can change it to the argument that defines in your tag
from django import template
register = template.Library()
#register.simple_tag
def change_a_text_filter(format_string, arg):
# find the url that made in urlize with regex
# change it with arg
# return the result
I was on a completely wrong road to solve this problem. I was trying to urlize a link from TextField, but didn't consider the fact that I only needed to implement html code as Visit link.com! in the TextField, and then use safe template tag to render html directly as below:
{{ text.value|safe }}
So in this solution, there is no need to urlize, and of course there is no need to extend this tag neither.
NOTE: As commented by #rahimz (link to comment) I understand that there are safety concerns regarding safe tag, So I should emphasize that only me and a company-trusted admin will have access to admin panel and there is no worries that this admin will send malicious code through this TextField.

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 template filter ,use 2 or more filter like pipe

I want to use more than one filter on template like below:
value: {{ record.status|cut:"build:"|add:"5" }}
where record.status would be build:n, 0 < n< 100
but I want to add this value a base value 5.
I tried above code, it only take effect on the first filter,
so I did not get the value plus 5.
Does django only support one filter?
Thanks
First, the answer for your question "Does django only support one filter?" is that
Django does support almost unlimited number of chained filters (depends on your platform and ability to write that number of chained filters of course =) . Take some code for example (not proof but it makes sense), it is actually a template '{{ x|add:1|add:1|...10000 in all...|add:1 }}'
>>> from django.template import *
>>> t = Template('{{ x|'+'|'.join(['add:1']*10000)+' }}')
>>> t.render(Context({'x':0}))
u'10000'
Second, please check the template to ensure that you are using built-in version of cut and add; also check the output value after the cut to ensure it can be coerced to int w/o raising exception.
I've just checked and found that even the Django 0.95 supports this usage:
def add(value, arg):
"Adds the arg to the value"
return int(value) + int(arg)
Chaining filters is supported. If you want to figure why it doesn't work, then what I'd do is:
install ipdb
in django/templates/defaultfilters.py, find "def add", and put "import ipdb; ipdb.set_trace()" at the top of the function
open the page in the browser again, you should be able to follow the execution of the code from the terminal that runs runserver and figure why you're not getting the expected results
An easier way is to make your own template filter. It could look like
from django.template import Library
register = Library()
#register.filter
def cut_and_add(value, cut, add):
value = value.replace(cut, '')
value = int(value) + add
return value
Suppose you saved this in yourapp/templatetags/your_templatetags.py (and that yourapp/templatetags/__init__.py exists - it can be empty). Then you would use it in the template as such:
{% load your_templatetags %}
{{ record.status|cut_and_add:"build:",5 }}
Of course, this is untested, pseudo code. But with a little effort you could get it to work.

Use Django's url template tag with special characters

I'm using django's url tag to move from one view to another:
<a href = {% url wiki_view item %}>Read more here</a>
For most "items" this works perfectly. But I have an "item" with a / character: Sci-Fi/Fantasy. In this instance, I get an error
Caught NoReverseMatch while rendering: Reverse for 'wiki_view' with arguments '(u'Sci-fi/Fantasy',)' and keyword arguments '{}' not found.
My urls.py is defined as such:
url(r'^wiki/page/(?P<page_title>[^/]*)/$', views.wiki_view, name = 'wiki_view'),
Is there a way for this to work with a "/" character in place like this?
Use the urlencode filter as Ignacio suggests, however you are still getting a probelm as by default that filter assumes a / to be left alone. You can solve this (if you are using the dev version currently) by using the filter in the following way:
item|urlencode:""
This is explained in the Django docs.
Pass the argument through urlencode first.
You should be using Djanog's slugify on anything which you are using in a URL. It replaces characters like that with URL friendly ones, and human readable alternatives.
from django.template.defaultfilters import slugify

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