Django template tag to truncate text - django

Django has truncatewords template tag, which cuts the text at the given word count. But there is nothing like truncatechars.
What's the best way to cut the text in the template at given char-length limit?

This has recently been added in Django 1.4. e.g.:
{{ value|truncatechars:9 }}
See doc here

{{ value|slice:"5" }}{% if value|length > 5 %}...{% endif %}
Update
Since version 1.4, Django have a built-in template tag for this:
{{ value|truncatechars:9 }}

I made my own template filter, that add "..." to the end of (last word of) the (truncated) string as well:
from django import template
register = template.Library()
#register.filter("truncate_chars")
def truncate_chars(value, max_length):
if len(value) > max_length:
truncd_val = value[:max_length]
if not len(value) == max_length+1 and value[max_length+1] != " ":
truncd_val = truncd_val[:truncd_val.rfind(" ")]
return truncd_val + "..."
return value

If you go on creating your own custom template tag, consider to leverage the native Django util Truncator.
The following is a sample usage of the Truncator util:
>>> from django.utils.text import Truncator
>>> Truncator("Django template tag to truncate text")
<Truncator: <function <lambda> at 0x10ff81b18>>
>>>Truncator("Django template tag to truncate text").words(3)
u'Django template tag...'
Truncator("Django template tag to truncate text").chars(20)
u'Django template t...'
And here how you can use it inside a Django template tag:
from django import template
from django.utils.text import Truncator
register = template.Library()
#register.filter("custom_truncator")
def custom_truncator(value, max_len, trunc_words=False):
""" Set trunc_words=True to truncate by words instead of by chars."""
truncator = Truncator(value)
return truncator.words(max_len) if trunc_words else truncator.chars(max_len)
Eventually you can import the custom_truncator in any Django template.

Here it is in the Django Documentation, Built-in template tags and filters: truncatechars

You should write a custom template filter: http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters
Have a look at how truncatewords is built in django.utils.text

You can achieve your goal with similar code:
{{ value_of_text|truncatechars:NUM_OF_CHARS_TO_TRUNCATE}}
where NUM_OF_CHARS_TO_TRUNCATE is number of chars to leave.

slice

Adding a "truncate" filter was a feature request for 4 years but finally landed in trunk, as far as I understand https://code.djangoproject.com/ticket/5025 - so we’ve to wait for the next release or use trunk.

Related

Django url template with query parameters

I'm trying to pass query parameters via my view into a link, but it escapes me on how to actually achieve this in a good way.
My template is as following:
<a class="link-button" href="{% url 'videos:index' %}?tag={{ tag }}&page={{ next }}">Next</a>
This returns what I want:
http://127.0.0.1:8000/videos/?tag=1&page=2
While this works, it's quite fragile, does not handle None values and there must be a better way of doing this.
I tried to pass this via the urltemplate tag but it did not seem to be what I was looking for since it requires url config changes for path:
{% url 'videos:index' page=next tag=tag %}
Is there an actual way of doing this or a template tag I can use to get the parameters? I tried searching for this but it gave me a lot of old results and more path urls, like: /videos/page-1/tag-1/ which I'm not looking for.
I was hoping to do something like:
Next
There is no builtin support, but you can add one yourself. You can for example define the following template tag. We can for example construct files in boldface:
app/
templatetags/
__init__.py
urlparams.py
Where in urlparams.py, we define:
from django import template
from urllib.parse import urlencode
register = template.Library()
#register.simple_tag
def urlparams(*_, **kwargs):
safe_args = {k: v for k, v in kwargs.items() if v is not None}
if safe_args:
return '?{}'.format(urlencode(safe_args))
return ''
In the template, we can then load the template tag and then use it like with:
{% load urlparams %}
Next
Note that strictly speaking, the URL parameters can contain the same key multiple times. This is here not possible. So we can not generate all possible URL parameters, but this is usually quite rare, and in my opinion not a good idea in the first place.
you can use default template filter and update your example
<a class="link-button" href="{% url 'videos:index' %}?tag={{ tag|default:'' }}&page={{ next|defaul:'' }}">Next</a>
output for empty tag and page is:
http://127.0.0.1:8000/videos/?tag=&page=
but if you want to dont print None Tag in url you must your own template tag or filter. simply you can write this template filter
#register.filter
def print_query_param(value, key)
if value and key:
return "%s=%s&" % (key, value)
and you can use it as below
<a class="link-button" href="{% url 'videos:index' %}?{{ tag|print_query_param:'tag' }}{{ next|print_query_param:'page' }}">Next</a>
Template tag urlencode
Use the template tag urlencode:
example
Small note:
Note that there is no need to pass the param names themselves through urlencode, since in this case they are literals. If the param names were not literals, you would need to, like this:
example

django template tag shorten string

I have the following in a template -
<tes:productiondate>{% now "Y-m-d" %}T{% now "H:i:s" %}-{{{% now "u" %}|truncatechars:4}}</tes:productiondate>
It's giving me an error
Could not parse some characters: |{% now "u" %}||truncatechars:4
{% now "u" %} does display correctly the problem is that by default it displays 6 characters and I only want it to display 4 characters.
I'm realizing that truncatechars it's the right way to do it because I don't want the "..." so how do I go about shortening the string of 6 characters to be only 4?
You can't apply a filter to template tag's output. In trunk version of django {% now %} tag can save formatted time to variable:
{% now "u" as msec %}{{ msec|truncatechars:4 }}
But in the current stable django (1.7.2) the as keyword is not supported.
So you have to write custom template tag. It is easy:
import datetime
from django import template
register = template.Library()
#register.simple_tag
def microseconds(format_string):
return datetime.datetime.now().strftime('%f')[:4]

Using AngularJS template tags in Django [duplicate]

I want to use AngularJS with Django however they both use {{ }} as their template tags. Is there an easy way to change one of the two to use some other custom templating tag?
For Angular 1.0 you should use the $interpolateProvider apis to configure the interpolation symbols: http://docs.angularjs.org/api/ng.$interpolateProvider.
Something like this should do the trick:
myModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
Keep in mind two things:
mixing server-side and client-side templates is rarely a good idea and should be used with caution. The main issues are: maintainability (hard to read) and security (double interpolation could expose a new security vector - e.g. while escaping of serverside and clientside templating by themselves might be secure, their combination might not be).
if you start using third-party directives (components) that use {{ }} in their templates then your configuration will break them. (fix pending)
While there is nothing we can do about the first issue, except for warning people, we do need to address the second issue.
you can maybe try verbatim Django template tag
and use it like this :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
{% verbatim %}
<div ng-app="">
<p>10 is {{ 5 + 5 }}</p>
</div>
{% endverbatim %}
If you did separate sections of page properly then you can easily use angularjs tags in "raw" tag scope.
In jinja2
{% raw %}
// here you can write angularjs template tags.
{% endraw %}
In Django template (above 1.5)
{% verbatim %}
// here you can write angularjs template tags.
{% endverbatim %}
We created a very simple filter in Django 'ng' that makes it easy to mix the two:
foo.html:
...
<div>
{{ django_context_var }}
{{ 'angularScopeVar' | ng }}
{{ 'angularScopeFunction()' | ng }}
</div>
...
The ng filter looks like this:
from django import template
from django.utils import safestring
register = template.Library()
#register.filter(name='ng')
def Angularify(value):
return safestring.mark_safe('{{%s}}' % value)
So I got some great help in the Angular IRC channel today. It turns out you can change Angular's template tags very easily. The necessary snippets below should be included after your angular include (the given example appears on their mailing lists and would use (()) as the new template tags, substitute for your own):
angular.markup('(())', function(text, textNode, parentElement){
if (parentElement[0].nodeName.toLowerCase() == 'script') return;
text = text.replace(/\(\(/g,'{{').replace(/\)\)/g, '}}');
textNode.text(text);
return angular.markup('{{}}').call(this, text, textNode, parentElement);
});
angular.attrMarkup('(())', function(value, name, element){
value = value.replace(/\(\(/g,'{{').replace(/\)\)/, '}}');
element[0].setAttribute(name, value);
return angular.attrMarkup('{{}}').call(this, value, name, element);
});
Also, I was pointed to an upcoming enhancement that will expose startSymbol and endSymbol properties that can be set to whatever tags you desire.
I vote against using double parentheses (()) as template tag. It may work well as long as no function call is involved but when tried the following
ng:disabled=(($invalidWidgets.visible()))
with Firefox (10.0.2) on Mac I got a terribly long error instead of the intended logic. <[]> went well for me, at least up until now.
Edit 2012-03-29:
Please note that $invalidWidgets is deprecated. However I'd still use another wrapper than double braces. For any angular version higher than 0.10.7 (I guess) you could change the wrapper a lot easier in your app / module definition:
angular.module('YourAppName', [], function ($interpolateProvider) {
$interpolateProvider.startSymbol('<[');
$interpolateProvider.endSymbol(']>');
});
API docs.
You could always use ng-bind instead of {{ }}
http://docs.angularjs.org/api/ng/directive/ngBind
<span ng-bind="name"></span>
I found the code below helpful. I found the code here: http://djangosnippets.org/snippets/2787/
"""
filename: angularjs.py
Usage:
{% ng Some.angular.scope.content %}
e.g.
{% load angularjs %}
<div ng-init="yourName = 'foobar'">
<p>{% ng yourName %}</p>
</div>
"""
from django import template
register = template.Library()
class AngularJS(template.Node):
def __init__(self, bits):
self.ng = bits
def render(self, ctx):
return "{{%s}}" % " ".join(self.ng[1:])
def do_angular(parser, token):
bits = token.split_contents()
return AngularJS(bits)
register.tag('ng', do_angular)
If you use django 1.5 and newer use:
{% verbatim %}
{{if dying}}Still alive.{{/if}}
{% endverbatim %}
If you are stuck with django 1.2 on appengine extend the django syntax with the verbatim template command like this ...
from django import template
register = template.Library()
class VerbatimNode(template.Node):
def __init__(self, text):
self.text = text
def render(self, context):
return self.text
#register.tag
def verbatim(parser, token):
text = []
while 1:
token = parser.tokens.pop(0)
if token.contents == 'endverbatim':
break
if token.token_type == template.TOKEN_VAR:
text.append('{{')
elif token.token_type == template.TOKEN_BLOCK:
text.append('{%')
text.append(token.contents)
if token.token_type == template.TOKEN_VAR:
text.append('}}')
elif token.token_type == template.TOKEN_BLOCK:
text.append('%}')
return VerbatimNode(''.join(text))
In your file use:
from google.appengine.ext.webapp import template
template.register_template_library('utilities.verbatim_template_tag')
Source:
http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html
You can tell Django to output {{ and }}, as well as other reserved template strings by using the {% templatetag %} tag.
For instance, using {% templatetag openvariable %} would output {{.
I would stick with a solution that uses both django tags {{}} as well angularjs {{}} with a either a verbatim section or templatetag.
That is simply because you can change the way angularjs works (as mentioned) via the $interpolateProvider.startSymbol $interpolateProvider.endSymbol but if you start to use other angularjs components like the ui-bootstrap you will find that some of the templates are ALREADY built with standard angularjs tags {{ }}.
For example look at https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html.
If you do any server-side interpolation, the only correct way to do this is with <>
$interpolateProvider.startSymbol('<{').endSymbol('}>');
Anything else is an XSS vector.
This is because any Angular delimiters which are not escaped by Django can be entered by the user into the interpolated string; if someone sets their username as "{{evil_code}}", Angular will happily run it. If you use a character than Django escapes, however, this won't happen.

Format of timesince filter

Is there a way to use the {{date|timesince}} filter, but instead of having two adjacent units, only display one?
For example, my template is currently displaying "18 hours, 16 minutes". How would I get it to display "18 hours"? (Rounding is not a concern here.) Thank you.
I can't think of a simple builtin way to do this. Here's a custom filter I've sometimes found useful:
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
#register.filter
#stringfilter
def upto(value, delimiter=None):
return value.split(delimiter)[0]
upto.is_safe = True
Then you could just do
{{ date|timesince|upto:',' }}
Since the timesince filter doesn't accept any arguments, you will have to manually strip off the hours from your date.
Here is a custom template filter you can use to strip off the minutes, seconds, and microseconds from your datetime object:
#this should be at the top of your custom template tags file
from django.template import Library, Node, TemplateSyntaxError
register = Library()
#custom template filter - place this in your custom template tags file
#register.filter
def only_hours(value):
"""
Filter - removes the minutes, seconds, and milliseconds from a datetime
Example usage in template:
{{ my_datetime|only_hours|timesince }}
This would show the hours in my_datetime without showing the minutes or seconds.
"""
#replace returns a new object instead of modifying in place
return value.replace(minute=0, second=0, microsecond=0)
If you haven't used a custom template filter or tag before, you will need to create a directory in your django application (i.e. at the same level as models.py and views.py) called templatetags, and create a file inside it called __init__.py (this makes a standard python module).
Then, create a python source file inside it, for example my_tags.py, and paste the sample code above into it. Inside your view, use {% load my_tags %} to get Django to load your tags, and then you can use the above filter as shown in the documentation above.
Well, you can make use of JS here. You need to add a script that splits the first and second units and then displays only the first one.
Say the part of your template looks like this:
<your_tag id="your_tag_id">{{date|timesince}}</your_tag>
Now, add the below script to your template.
<script>
let timesince = document.getElementById("your_tag_id").innerHTML.split(",");
document.getElementById("your_tag_id").innerHTML = timesince[0];
</script>
A quick and dirty way:
Change the django source file $PYTHON_PATH/django/utils/timesince.py #line51(django1.7) :
result = avoid_wrapping(name % count)
return result #add this line let timesince return here
if i + 1 < len(TIMESINCE_CHUNKS):
# Now get the second item
seconds2, name2 = TIMESINCE_CHUNKS[i + 1]
count2 = (since - (seconds * count)) // seconds2
if count2 != 0:
result += ugettext(', ') + avoid_wrapping(name2 % count2)
return result

Django adds %2f in place of the / in the template for an image field

I am having a few issues with a django template with a mimetype="text/plain".
Firstly the s3 part of url is rendering with the :80 on the end and then the actual image url is rendering with '%2f' in replace of each slash.
object.image.url
I have tried safe and other custom tags to replace the '%2f' and it just wont work
#what I have
http://blahblah.s3.amazonaws.com:80/navigation%2Fprimary%2Fimage.jpg
#what I want
http://blahblah.s3.amazonaws.com/navigation/primary/image.jpg
The custom tag I have tried along side safe is:
import re
from django import template
register = template.Library()
def reslash (value):
return value.replace('%2f', '/')
register.filter('reslash', reslash)
used like this:
{{ object.image.url|reslash }}
But it doesn't work. Thanks
Django automatically html escapes all variables in templates. In order to insert the value of a variable without escaping you should use the safe filter to tell django the value does not need to be auto-escaped like so:
{{ object.image.url|safe }}