How do I avoid duplicating strings in my Django templates? - python

I'm wondering how to duplicate a few strings in my templates. Specifically, I'm looking to create a table of contents sort of navigation at the top of my pages with anchor links to content farther down (like http://www.google.com/transparencyreport/faq/). I want the links to have the same text as the section headers farther down.
I've thought about using {% with %}, but it seems unwieldy to have to nest everything inside my {% with %} block.
Similar to Whats the best way to duplicate data in a django template?, but I am not inheriting this template anywhere so using {% block %} is not really an option.

This seems like a situation for just using a template variable that you've passed from a view (e.g. {{ link_name }}).
You could use also possibly use template inclusion tag that includes another template with your duplicate information.

In your view, you could potentially break your content up so that the headers are individually accessible as template variables. You might store the information associated with each header as a list of dicts:
page_content = [
{
'id':'header1',
'header': 'Text for Header 1'
'content' : 'Content Beneath header 1'
},
]
Then, in your templates, you could generate your table on contents with something like this:
{% for d in page_content %}
{{ d.header }}
{% endfor %}
While the content of your page would look something like this:
{% for d in page_content %}
<h1 id="#{{ d.id }}">{{ d.header }}</h1><p>{{ d.content }}</p>
{% endfor %}

Related

Jinja List Issue

I am getting a weird problem in Jinja, I have a list endpoints, which contains dictionary for every endpoint. In each dictionary, there is a key tags which is a list. Every item in tags is itself a dictionary where the key value gives the label of a tag. endpoint may have similar tags.
A sample abstract representation of an endpoints object can be:
[ {"tags":[{"value":"car"},{"value":"place"}]} , {"tags":[{"value":"van"},{"value":"place"}]} ]
what I want is to simple display unique tags in a div. It is simple, keeping a list of all displayed tags and upon getting a tag, checking if it is already in the list, and if not display it and add it to the list. Weirdly, it's not working.
The codes are:
{% set tagValues = [] %}
{% for endpoint in endpoints %}
{% for tag in endpoint["tags"]%}
{% set tagValue = tag["tag"]["value"] %}
{% if tagValue not in tagValues %}
{% set tagValues = tagValues + [tagValue] %}
<span >{{ tagValue }}</span></a>
{% endif %}
{% endfor %}
{% endfor %}
it is not working, for example, for the enpoints list above, I am getting the following output:
car place van place
is there any problem with the codes ?
I recommend creating a distinct list of tags in your View. e.g.
distinctTags = list(set([tag for endpoint in endpoints for tag in endpoint]))
and passing that to your template
{% for tag in distinctTags %}
<span >{{ tagValue }}</span></a>
{% endfor %}
this has the advantage of the distinct tag code being reusable and the code being less procedural.
my jinja knowledge is limited, but by adding tagValues to the output, it appears that it's reset after each iteration of the outer loop. I'd guess it's to do with scopes, but don't know.
My recommendation would be to pre-process your endpoints in regular python before passing to jinja

Flask redirect on select onchange

I'm a beginner to flask and I'm making a small web scraper app. What I've done so far has created a dropdown with a list of elements. Now I want to be able to render another page when the user selects a value from the list and I want to pass that value to the next page as well.
{% extends "base.html" %}
{% block content %}
<select id = "foo" onchange="">
{% for item in Citydata %}
<option value = {{ item.link }}> {{ item.name }} </option>
{% endfor %}
</select>
{% endblock %}
This makes the list and adds in all the links and values. I know that what should happen is that when an option is selected a new route is selected/used and a new template file is loaded. But I don't know how to do it.
If you are trying to do a form submission, follow colidyre's suggestion.
It seems to me, however, that you're looking to add a variable in your path, so you will need to use Flask's Variable Rules
It is unclear what item.link is, however if you have formatted that to be associated with an item's id such as /item/1, you could create an <a> tag like so:
{% for item in Citydata %}
{{ item.name }}
{% endfor %}
That would handle populating the href properly on the front-end, next you will need to set up the proper route on the server to handle the path:
#app.route('/item/<int:item_id>')
def item_route(item_id):
# do some stuff
NOTE: you don't have to create <a> tags, but it is a bit more straight forward than <option>. To stick with <option> you would just need to add some JavaScript client-side to call the back-end service based on the selected <option>'s value attribute instead.

Return items from RSS feed in Django Templatetags

Django noob here.
I'm trying to add RSS feed items into a django template using templatetags (with classytags).
Here's my code:
from django import template
from classytags.core import Tag
import feedparser
register = template.Library()
class ExampleTag(Tag):
name = 'exampletag'
def render_tag(self, context):
raw_feed = "example.com/feed.rss"
feed = feedparser.parse(raw_feed)
entrylist = {}
for entry in feed.entries:
entrylist[entry.title]
return entrylist
register.tag(ExampleTag)
Then, in the template I can call the ExampleTag with:
{% load my_tag %}
{% exampletag %}
This results in a KeyError at / u'The First Entry In The Feed'
If I change my code to append to a list, the template renders without error and the entire structured list is output in a single string.
This is what I'd like to do:
{% load my_tag %}
{% for item in exampletag %}
<p> {{ item }} </p>
{% endfor %}
However this just fails silently (obviously I'm not passing an interable object to the template)
Any ideas? Is this even a good way to go about doing this?
Thanks in advance.
This code looks highly suspect:
for entry in feed.entries:
entrylist[entry.title]
Shouldn't it be something like this?
for entry in feed.entries:
entrylist[entry.title] = entry # or some value
As it is right now you are trying to index into an empty dictionary and are thus getting a KeyError exception.
But I'm still not sure what you are trying to do. Here are 2 ideas that come to mind that may get you started.
Idea one: it sort of looks like you should write an inclusion tag.
Something like (untested):
#register.inclusion_tag('feed_entries.html'):
def feed_entries():
feed = feedparser.parse('example.rss')
return {'items': feed}
And in feed_entries.html
{% for item in items %}
<p> {{ item }} </p>
{% endfor %}
Then, in some random template where you want the list of items displayed:
{% load feed_tags %}
...
<p>Here are the latest entries:</p>
{% feed_entries %}
...
This is assuming feed contains a list of items you want to render somehow. Thus, whenever you use {% feed_entries %} in a template, your snippet of Python is called, it takes the returned dictionary and renders the feed_entries.html template, and the resulting HTML is placed wherever you wrote {% feed_entries %}.
Idea two: If you really want your tag to return a list of items, you could use an assignment tag:
#register.assignment_tag
def feed_entries():
return feedparser.parse('example.rss')
Then in your template you have to "catch" the result of this tag (the list of items):
{% feed_entries as items %}
{% for item in items %}
<p>{{ item }}</p>
{% endfor %}
But that means you'll have to duplicate the "as" and for-loop stuff in every template. The inclusion tag may save you typing and maintenance if you use it in many templates. But if you wanted to render the list differently in each template it would be more flexible. Say you want it in a list of <p> tags in one, but in a <ul> in another.

Checking if something exists in items of list variable in Django template

I have a list of sections that I pass to a Django template. The sections have different types. I want to say "if there is a section of this type, display this line" in my template, but having an issue. What I'm basically trying to do is this.
{% if s.name == "Social" for s in sections %}
Hello Social!
{% endif %}
But of course that's not working. Any idea how to basically in one line loop through the items in a list and do an if statement?
ADDITIONAL INFO: I could potentially have multiple "Social" sections. What I'm trying to do in the template is say "if there are any social sections, display this div. If not, don't display the div." But I dont want the div to repeat, which is what would happen with the above code.
Ideally what you would do is create a list that the template gets as such:
l = [s.name for s in sections]
And in the template, use:
{% if 'Social' in l %}
You're trying to put more logic into a template than they are meant to have. Templates should use as little logic as possible, while the logic should be in the code that fills the template.
You can't use list comprehensions in templates:
{% for s in sections %}
{% if s.name == 'Social' %}
Hello Social!
{% endif %} {# closing if body #}
{% endfor %} {# closing for body #}
{% if sections.0.name == "Social" %}
Hello Social!
{% endif %}

Render Externally Defined Block In Django Template

I'm writing a simple blog-like application for Django and am trying to get the effect of having a front page with posts limited to 5, with a comprehensive archive that lists something like 100 posts at a time. (100 is not realistic, just throwing a number out there)
Since the blog post blocks will look exactly the same between the two pages minus the number being shown, I'd like to put the corresponding HTML in a separate template that I can include or link to from the actual templates being rendered. I've looked over the documentation, and the include tag looked promising, but it apparently renders outside of the current context, which is not helpful to my cause, since it wouldn't get the objects to loop through. Outside of that, I can't see any other way to do what I want. Is this possible or am I just out of luck and going to have to violate DRY? Code is below to give you an idea of what I want.
Thanks
#######################
# news/frontpage.html #
#######################
{% extends "news/base.html" %}
{% block site_title %} - Front Page{% endblock %}
{% block center_col %}
{{ block.super }}
View Older Blog Posts
{% endblock %}
{% block blog_rows %}
{% for object in object_list %}
# Blog post content would go here, however it is to be included.
{% endfor %}
{% endblock %}
You're looking for an inclusion tag.
Why don't you filter for the blog posts you want to show in your view? That way you can keep the template the same:
{% for object in blogposts %}
# ...
{% endfor %}
You define blogposts in your view, which either includes 5 or 100 posts.
Ignacio is right that you want an inclusion tag, but you should know that the include tag does not render outside the current context - it very definitely uses the same context as the block it's in.
Your problem is probably that you're trying to call blogpost_set on the object_list - but the relationship is not with the list of objects, it's with each individual object in the list. You'd need to iterate through object_list and then through blogpost_set.all on each one.

Categories