HubL template language list logic - python

I'm working in a cms called Hubspot using a templating language called HubL. Hubspot is written in python and HubL closely resembles Jinja2 or Django templates. Usually when I need to find an answer I can search for the answer in jinja2 and find what I need. I have been trying to figure this out for a few weeks now though but I think the issue is to specific.
Hubspot hosts blogs. These blogs accessible under the "contents" variable in a blog template or by using a pre-defined function on any other template type:
{% for content in contents %}
{# output blog list #}
{% endfor %}
or
{% set test_list = blog_recent_posts('default', 250) %}
{% for post in test_list %}
{# output blog list #}
{% endfor %}
Each blog post has a topics_list variable. this variable contains a list of topics set to a specific blog. you would use it inside the blog list loop by looping through it.
{% for content in contents %}
{% for topic in content.topic_list %}
{{ topic.name }}
{% endfor %}
{% endfor %}
If you just call topic_list:
{% for content in contents %}
{{ content.topic_list }}
{% endfor %}
it outputs:
[topic1, topic2]
The issue I am having is that I need to exclude posts with specific topics from the main list. I can use a nested for loop and a condition to exclude then from output:
{% for content in contents %}
{% for topic in content.topic_list %}
{% unless topic.name = 'topic 1' %}
{# ouput if condition is not met #}
{% endunless %}
{% endfor %}
{% endfor %}
This is messy. It loops through the blogs and at each index loops through the topics. if a topic matches the defined topic then it is omitted from output. This works, except that it doesn't omit the blog from contents. The blogs that are block from output still exist at their index. If I limit the output to 3 blogs, for instance, and one of the three blogs has a topic that matches 'topic 1', the space isn't filled with the next blog in line, the space is just left blank.
Actually what this is doing is outputting the markup for a list item for each topic of each blog as long as that topic doesn't match the blocked topic so it will duplicate posts if more than one topic is present.
I thought about using the predefined function, which you can use to define a specific topic you want posts for, so I looped through a global list of topics tied to the blog, and if the topic doesn't equal blocked topics it runs the topic specific function:
{% set mlist = [] %}
{% set global_topics = blog_topics('default', 250) %}
{% for gtopic in global_topics %}
{% unless gtopic == 'topic1' %}
{% set wlist = blog_recent_posts('default', 250, gtopic.slug) %}
{% set mlist2 = mlist.append(wlist) %}
{% endunless %}
{% endfor %}
{% for post in mlist[0] %}
<strong>{{post.name }}</strong><br>
{% for topic in post.topic_list %}
{{topic.name}}<br>
{% endfor %}<br> <br>
{% endfor %}
I'm honestly surprised I got this to work period but it combines the list created from each topic and outputs them. The only issue is that because multiple topics are applied to the topic lists, while it doesn't include a list created from the blocked topic, it still includes blogs containing the blocked topic from lists created using the second or third (etc.) topic on the blog post.
The simplest solution would be to create a new list by filtering post by if the topic is IN the topic_list but this doesn't work.
{% set mlist = [] %}
{% for content in contents %}
{% unless 'topic-1' is in content.topic_list %}
{% set mlist2 = mlist.append(content) %}
{% endunless %}
{% endfor %}
but I guess you can't use 'in' like this? it doesn't work.
Please keep in mind that I understand that this would be a non-issue if I did this filtering in python directly instead of in the template, and I understand that logic shouldn't be handled in the template, but Hubspot doesn't allow custom python scripting or any back end capability so this is what I have to work with. Can anyone think of a creative solution?

Related

Django TemplateSyntaxError in with template tag

I'm trying to use this app in my project.
https://github.com/streema/django-favit
I already can use the fav-unfav part of this app. I also want to list favourites of user for every user. In read me part it says use this and it will be listed but I have an error with
{% with user_favorites <user> "baslik.Entry" as favorite_list %}
{% for fav_obj in favorite_list %}
{{ fav_obj }}
{% endfor %}
{% endwith %}
Error:
TemplateSyntaxError at /
u'with' expected at least one variable assignment
This is the template tag part for user_favorites:
#register.assignment_tag
def user_favorites(user, app_model=None):
"""
Usage:
Get all user favorited objects:
{% with user_favorites <user> as favorite_list %}
{% for fav_obj in favorite_list %}
{# do something with fav_obj #}
{% endfor %}
{% endwith %}
or, just favorites from one model:
{% with user_favorites <user> "app_label.model" as favorite_list %}
{% for fav_obj in favorite_list %}
{# do something with fav_obj #}
{%
{% endwith %}
"""
return Favorite.objects.for_user(user, app_model)
How can I get rid of this error? Thanks.
It's a reasonably common convention in documentation that anything in angle brackets is a placeholder to be replaced by the actual value. In this case, <user> is supposed to be replaced by the object containing the actual user.
{% with user_favorites request.user ...
I must say, though, that the documentation still doesn't make any sense. You can't use an assignment tag in a with statement like that - even after correcting the user issue, this still won't work. The confusing thing is that the same syntax is repeated throughout the documentation, but it simply doesn't work.
I think this is simply a bug with the documentation, and suspect that if you simply remove the word "with" this will work.
To use custom template tag in django, it is needed to explicitly load it in template.
Add this line at the beginnig of your template (but after {% extends ... %}, if you have such):
{% load favit_tags %}
Looks like this step is missed from django-favit README.

How to get with statement working with zinnia tag

I got a problem with iteration on zinnia tag outcome. Let's say that that tag returns a list of some categories, I tried to manage it in few ways:
{% with categories=get_plain_categories %}
{% for category in categories %}
<h1>{{ category }}</h1>
{% endfor %}
{% endwith %}
or simply:
{% for category in get_plain_categories %}
<h1>{{ category }}</h1>
{% endfor %}
But in both ways, it seems to not even run get_plain_categories tag (I made few prints in it), but when I write : {% get_plain_categories %}, it returns list as it's supposed to.
How should I get that working?
Unfortunatelly with tag is not that powerful, you can't use it with output of other tags. You'll have to create your own tag.
As an example you can have a look at the static. It lets you insert a path to a static file with {% static "images/hi.jpg" %} but you can't easily save it to a variable for later use. That's why in Django 1.5 it got a new syntax {% static "images/hi.jpg" as myphoto %} and this way you can later use {{ myphoto }}. This can't be achieved with with.
That said, I can't find any mentions of get_plain_categories in Google, which is weird.

Django templates: accessing the previous and the following element of the list

I am rather new to django templates and have an impression that I have not understood some basics.
I have a list of elements and I need to render an element of the list based on conditions against the the previous and the next elements (in case the following or the previous elements are hidden, I need to mark the current element as border element).
How can I reference the previous and the following elements within a for loop in Django templates?
You could write a custom template filter for next and previous:
def next(value, arg):
try:
return value[int(arg)+1]
except:
return None
and in the template:
{% for ... %}
{% with list|next:forloop.counter0 as next %}
{% if next.is_hidden %}
...
{% endif %}
{% endwith %}
{% endfor %}
but like others have said, there are probably more elegants solutions doing this via your view
You can't do this strictly with built-in template tags. You need to involve some Python.
One method would be to zip the list with itself:
new = ([(None, orig[0], orig[1])] +
zip(orig, orig[1:], orig[2:]) +
[(orig[-2], orig[-1], None)])
and pass that to the template, then loop over it like this:
{% for prev, current, next in new %}
{% if prev.hidden or next.hidden %}
BORDER
{% endif %}
{{ current }}
{% endfor %}
You really shouldn't use the Django templates for this kind of logic. You have your views to handle it. I would figure out which elements of the list are borders and pass in an extra parameter that I can handle in the template using a simple if statement. For example:
{% for element,is_border in data.items %}
{% if is_border %}
do something
{% endif %}
{% endfor %}
There are tons of similar ways you can do this. I presented just one example.
You can create an external tag which does that but django templating system which was build to be lightweight has no such feature in for loops.

Dynamic session access in templates

I'm trying to access session keys within a loop that needs to be dynamic, I think you'll get what I'm going for by looking at my code that isn't working.
{% for q in questions %}
<div class="question_wrap">
<h2>{{ q }}</h2>
# this does not work
{% if not request.session.get(str(q.id), False) %}
<!-- show them vote options -->
{% else %}
<!-- dont show options -->
{% endif %}
</div>
{% endfor %}
The syntax of Django templates is very limiting in order to prevent people from putting too much logic inside templates and doesn't allow you to pass parameters to methods.
You can prepare a list of tuples already in the view or write a simple template tag for that. The first options is usually easier:
In the view:
questions = [(q, request.session.get(str(q.id), False)) for q in questions]
In the template:
{% for q, has_voted in questions %}
...
{% endfor %}

Blocks within blocks

I'm having problems displaying nested blocks in a template.
eg.
{% for category in categories %}
//code to display category info
{% products = products.object.filter(category = category) %}
{% for product in products%}
//code to display product info
{% endfor %}
{% endfor %}
I'm getting a "Invalid block tag: 'endfor'" error.
Any ideas?
You cannot assign to variables in the Django template system. Your two attempts:
{% products = products.object.filter(category = category) %}
and
{% products = category.get_products %}
are both invalid Django syntax.
Some Python templating systems are PHP-like: they let you embed Python code into HTML files. Django doesn't work this way. Django defines its own simplified syntax, and that syntax does not include assignment.
You can do this:
{% for category in categories %}
//code to display category info
{% for product in category.get_products %}
//code to display product info
{% endfor %}
{% endfor %}
I think you cannot use arguemnts for methods. You have to modify your categories object, so that you kann use:
{% for product in category.products %}
{% products = products.object.filter(category = category) %}
is not recognized as a valid tag in the django template system. Therefore django complains about the missing endfor, although the {% for x in y %) is not the error.
This should work
{% for category in categories %}
{% for product in products.object.all %}
//code to display product info
{% endfor %}
{% endfor %}
But this is not that, what you want to achieve. Simply you are not able to filter on product.objects with the argument category.
You have to write your own tag which takes arguments on filtering or rethink your problem.

Categories

Resources