I'm developing a project on Google AppEngine, using Django templates, so I have to use tags like {{ aitem.Author }} to print content within my HTML template.
Author, however, can either be a string or a list object, and I have no way to tell it in advance. When the Author is a list and I try to print it on my template, I get the ugly result of
Author: [u'J. K. Rowling', u'Mary GrandPr\xe9']
Is there any way to handle this kind of scenario (basically printing a field differently depending on its type) effectively? Do I have to rely on custom tags or any other means?
I think the cleanest solution would be to add a method to the model get_authors() which always returns a list either of one or more authors. Then you can use:
Author: {{ aitem.get_authors|join:", " }}
If you for some reason have only access to the templates and can't change the model, then you can use a hack like this:
{% if "[" == aitem.Author|pprint|slice:":1" %}
Author: {{ aitem.Author|join:", " }}
{% else %}
Author: {{ aitem.Author }}
{% endif %}
P.S. it's not a good convention to use capital letters for attribute names.
I think that Aidas's get_authors() solution is the best, but an alternative might be to create a template tag that does the test. You'll want to read up on custom template tags, but they aren't that hard to create if you look at the existing ones.
I followed Matthew's advice and eventually implemented a filter to handle lists.
I'm posting it here just in case someone else needs it.
#register.filter(name='fixlist')
def fixlist(author):
if type(author) == list:
return ', '.join(author)
else:
return author
I call it from the template pages like this {{ aitem.Author|fixlist }}
Thank you for the help!
Related
I generate a form via Flask-WTF.
The relevant checkbox looks like this:
conditions = BooleanField(
"I agree to the above conditions.", validators=[DataRequired()])
I'd like to have the I agree bold or strong - NOT the rest of the sentence.
I cannot pass in HTML tags, as they get escaped and rendered as text.
This would look like this:
[] <strong>I agree</strong> to the above conditions.
I'd like to get this result:
I agree to the above conditions.
Is this possible?
Thanks for any hints.
Thanks to the other answer by #gaefan, I read more about Jinja templating and the safe filter and came up with a another working solution.
from flask import Markup
label_for_conditions = Markup('<span class="customClass">I agree</span> to the above conditions.')
conditions = BooleanField(label_for_conditions, validators=[DataRequired()])
This solution does not need even a safe filter in the template.
This answer is inspired by the following discussion:
Passing HTML to template using Flask/Jinja2
It is not a perfect solution, as now HTML and Python is mixed in the form definition, but it looks like you have to make a compromise.
A simple solution would be to just do it in the template. Instead of:
{{ user_form.conditions.errors }}{{ user_form.conditions.label_tag }}<br />{{ user_form.conditions }}
do:
{{ user_form.conditions.errors }}<strong>I agree</strong> to the above conditions.<br />{{ user_form.conditions }}
I haven't tested this, but you may be able to do:
conditions = BooleanField(
"<strong>I agree</strong> to the above conditions.", validators=[DataRequired()])
and then, in the template:
{{ user_form.conditions.errors }}{{ user_form.conditions.label_tag|safe }}<br />{{ user_form.conditions }}
There are two solutions:
When creating the Field using render_kw.
When rendering the Field in the template (when the field is being called [form.field()]).
Initialize Field with style:
All the Field objects in WTForms have a render_kw arg in __init__.
render_kw (dict) – If provided, a dictionary which provides default keywords that will be given to the widget at render time.
When your template is being rendered, Jinja2 is going to read this render_kw.
In your case, you can define:
conditions = BooleanField(
"I agree to the above conditions.", validators=[DataRequired()],
render_kw={"style": "font-weight: bold;")
Rendering while templating
When Jinja2 is rendering, you can specify other rendering options by calling the field.
form.field()
__call__(**kwargs): Render this field as HTML, using keyword args as additional attributes.
[...]
In all of the WTForms HTML widgets, keyword arguments are turned to
HTML attributes, though in theory a widget is free to do anything it
wants with the supplied keyword arguments, and widgets don’t have to
even do anything related to HTML.
So in your template file, you can do something like that:
{{ form.field(style="font-weight: bold;") }}
Note that there is an exception for the class keyword, probably reserved by something else, then the key should be class_.
Source: Fields - WTForms Documencation
I'm using WTForm 2.3.3 on Flask 1.1.2 and I ended up using the following since piping the label itself, the label_tag, etc. to safe were not working for me but this does:
{{ field.label.text|safe }}
I would like to know if there is any way to sum values in a variable in a Django template using {% for obj in objects %}. Something like this:
for student in students:
notes += student.note
Using the mathfilters or something similar to make the sum
Thanks!
If you want sum values in template you can use with tag
please read this link for more information
https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#with
You can use add filter.
It is a not a good idea to do so anyway. You want to keep your logic inside the views and keep the rendering inside the templates.
You can also use django-mathfilters
{% load mathfilters %}
{{ num1 | addition:num2 }}
I am internationalizing (i18n) our django project, i.e. adding {% blocktrans %} to our templates. I know about using count and {% plural %} to have different strings for varaibles. However I have a string that has two variables that each need to be pluralized, i.e. 4 possible options.
For example, my string is "You have {{ num_unread }} unread message{{ num_unread|pluralize }} out of {{ total }} total message{{ total|pluralize }}"
How would I convert that to blocktrans tags?
After doing some more research and reading, specifically about gettext, I don't think this is possible. gettext documentation only allows one variable to control the pluralization. There are probably problems with having 2 variable pluralization, since in arabic, you'd have to have 36 different strings to translate.
In the end I just worked around my original problem, and split it into two strings.
I have a list of soccer matches for which I'd like to display forms. The list comes from a remote source.
matches = ["A vs. B", "C vs. D", "E vs, F"]
matchFormset = formset_factory(MatchForm,extra=len(matches))
formset = MatchFormset()
On the template side, I would like to display the formset with the according title (i.e. "A vs. B").
{% for form in formset.forms %}
<fieldset>
<legend>{{TITLE}}</legend>
{{form.team1}} : {{form.team2}}
</fieldset>
{% endfor %}
Now how do I get TITLE to contain the right title for the current form? Or asked in a different way: how do I iterate over matches with the same index as the iteration over formset.forms?
Thanks for your input!
I believe that in the Django template language there is no built-in filter for indexing, but there is one for slicing (slice) -- and therefore I think that, in a pinch, you could use a 1-item slice (with forloop.counter0:forloop.counter) and .first on it to extract the value you want.
Of course it would be easier to do it with some cooperation from the Python side -- you could just have a context variable forms_and_matches set to zip(formset.forms, matches) in the Python code, and, in the template, {% for form, match in forms_and_matches %} to get at both items simply and readably (assuming Django 1.0 or better throughout this answer, of course).
This is an addition to Alex's answer.
I did some reading after my comment on Alex's answer and found that it is important to get the management form (basically a meta-form with information about how many forms are in the formset) into your template for your submitted data to be treated as a formset rather than just a pile of forms. Documentation here.
The only two ways I know to get that into your template are:
Send the formset in addition to whatever data structure you made,
and then render the management form with {{ my_formset.management_form }}
Render the management form in your view and send that as an item to your template
Of course if you use Alex's first method, the formset is already available so you can add the management form directly.
I'm using Django-Tagging, and I don't exactly need a cloud, I just want a limited list of the most popular tags used in my blog entries.
Using the following:
[(tag.name, int(tag.count)) for tag in Tag.objects.usage_for_model(Post, counts=True)]
It returns an array (note I'm using Lorem Ipsum while I develop):
[(u'deposit', 5), (u'escorol', 1), (u'gratuitous', 8), (u'marquee', 2)]
But then to order and limit it I need to then do this:
sorted([(tag.name, int(tag.count)) for tag in Tag.objects.usage_for_model(Post, counts=True)], key=lambda k:k[1], reverse=True)[:10]
Is there a neater way to do this? I feel like there must be.
If you are using the latest version of django, you could use aggregation. http://docs.djangoproject.com/en/dev/topics/db/aggregation an example on that page ..
Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
Django's {% regroup %} template tag might be useful for this. Assuming tags is in the context of your template:
{% regroup tags|dictsort:"count" by count as sorted_tags %}
...
{% for count in sorted_tags %}
...
{% for tag in count %}
...
{% endfor %}
{% endfor %}
I find the following sorting code a bit more readable than the one you wrote. Of course it doesn't remove the source problem stated by abeyer
import operator
tags = Tag.objects.usage_for_model(Post, counts=True)
tags.sort(key=operator.attrgetter('count'), reverse=True)
If you need to pull all the tags anyway, and the limit to the top n is simply a presentation thing, Fragsworth's answer is probably the way to go. If you aren't using the remainder of the tags somewhere else, I'd argue that this is really something that should be happening in the database query...you would want to be doing an 'ORDER BY count DESC LIMIT n' on the query to avoid pulling a bunch of tags that you aren't going to use.
However, it appears that django-tagging is hard coded to always group/sort by tag ids and names. I'd say the right solution is to file a bug against that, and get an api exposed that will give you back the top n tags.
I use raw sql for this:
trends = Tag.objects.raw("select tagging_tag.id, count(object_id) as counter from tagging_tag left join tagging_taggeditem on tagging_tag.id = tagging_taggeditem.tag_id group by tagging_tag.id order by counter DESC limit 10")
My approach for getting top tags in Django-Tagging is:
top_tags = Tag.objects.annotate(num=Count('taggit_taggeditem_items')).order_by('-num')