Insert comma between list of element - django

I have a list of events (with links) that I want to show as comma separated like
event1, event2, event3
I tried the solution here but not working for me.
Here is the code :
<p>
{% for element in event.getelement.all() %}
<a href="{{ build_absolute_url(url('main:home_getlink',link.uri)) }}">
{{ element }} </a>
{% endfor %}
</p>

Do:
<p>
{% for element in event.getelement.all %}
<a href="{{ build_absolute_url(url('main:home_getlink',link.uri)) }}">
{{ element }}
</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
Also, what's up with that href? Are you not using named routes whereby you can simply leverage the {% url [route-name] [params] %} tag?

This shouldn't be a function call since it is inside template. Change below,
{% for element in event.getelement.all() %}
with
{% for element in event.getelement.all %}
And for rest, the solution link you've posted should work after that.
Edit:
If you do this,
{{ event.getelement.all|join:", " }}
So this might produce result something like,
GetElement object, GetElement object, GetElement object
to get the value of a specific attribute you have to add the attribute as well. Something like this,
{% for e in event.getelement.all %}
{{ e.<attr_name> }},
{% endfor %}
And this might produce something like,
event1, event2, event3,
Of course this is not an optimal solution because this is not the right way to do things when using any Framework.
Optimal solution:
What should be followed is that, do each and every logic in your views and send clean (pure) form of data to your templates. I mean send lists, dictionaries, objects, tuples should be sent to the templates. Hence creating a list of all the events and sending it templates though context.
Note: Following example is all based on assumptions.
View:
def xyz(request):
event = Event.objects.get(name='xyz')
context = {
'event_elements': [e.name for e in event.getelement.all()]
}
return render(request, 'xyz.html', context)
Template:
{{ event_elements|join:", " }}
Now this will definitely work.

Related

Passing parameters to template for NavBar menu in Django

I am trying to switch my head to start using MVC.
I have a Base HTML template, with an include NavBar code:
{% include "navBar.html" %}
So far, so good. Now, I want to send information to it (to the navBar.html template) regarding the Menu buttons. I have some simple buttons, and others with drop-button behavior.
My buttons are objects, they have information about name, href, position, etc, type (simple-button or drop-button)
So, I created a nested list in this way:
outer_list = []
for a in UserModule.objects.filter(user=user_id, is_active=True):
inner_list = []
inner_list.append(UserModule(a))
for b in Submodule.objects.filter(module=a.module, is_active=True):
inner_list.append(Submodule(b))
outer_list.append(inner_list)
return {'outer_list': outer_list}
So, my first element in every inner_list is the head of the possible drop-button or a simple button depending on his type attribute.
The list at the end is like this:
[0] Button-Simple-A
[1] Button-Drop-A => [sub-button-1, sub-button-2, sub-button-3]
[2] Button-Simple-B
and so on.
When I pass this outer_list to the template, the thing I have to do to arrange the buttons into the menu are crazy things. It has no sense to use MVC if I am going to write a lot of code in the template.
Until now, I am doing this over the template, and It is missing the first element identification, to recognize the button type, etc...
{% with isFirst=true %}
{% for inner_list in outer_list %}
{% for object in inner_list %}
{% if isFirst == true %}
<li><a href=#>Drop_parent</a></li>
{% else %}
<li><a href=#>Drop-Child</a></li>
{% endif %}
{% endfor %}
<li><a href=#>Static Button</a></li>
{% endfor %}
{% endwith %}
It is not finished at all, but I believe I am taking the wrong way.
Good advice will be thankful.
Thanks
Finally,
I got something that works. I would like to know if this is acceptable or there is another better way.
On the view side:
for a in UserModule.objects.filter(user=user_id, is_active=True):
x = Module.objects.get(id=a.module_id, is_active=True)
if x.type == 'drop-parent':
list.append(x)
for b in Submodule.objects.filter(module=a.module_id, is_active=True):
list.append(b)
list.append('drop_end')
return {'menu_list': list}
On the template site:
{% for object in menu_list %}
{% if object.type == 'drop-parent' %}
<li class="dropdown">
{{object.name}}
<div class="dropdown-content">
{% endif %}
{% if object.type == 'drop-child' %}
<a href=#>{{ object.name }}</a>
{% endif %}
{% if object == 'drop_end' %}
</div>
</li>
{% endif %}
{% endfor %}
Another question: Why adding the class identifier inside the list, I can not see the attributes in the template? I mean list.append(Module(x)) is not seen as an object Module inside template; otherwise, if I avoid adding the class I can access the attributes.
Thanks, I appreciate it

Get a random object of a Wagtail page model but not the current one

So I have this template context processor:
from cases.models import CasePage
def random_case(request):
case = CasePage.objects.live().order_by('?')
return {'random_case': case}
And in the template I do this:
{% for entry in random_case %}
{% if request.get_full_path != entry.get_url %}
{% if forloop.first %}
<a class="ajax-link project-next" href="{{ entry.get_url }}">
<div class="nav-project-title">{{ entry.title }}</div>
<div class="nav-title">next</div>
</a>
{% endif %}
{% endif %}
{% endfor %}
And this works but the problem is sometimes the object is the same as the page so nothing is displayed. It would be great if that one would be skipped in favour of the next entry. And it's also too much logic in the template for me. What would be the best way to move this logic into the context processor and make it work?
Make random_case a method of CasePage, and filter out the case with an ID equal to self.
class CasePage(Page):
# ...
def random_case(self):
return CasePage.objects.live().exclude(id=self.id).order_by('?').first()
You can then refer to this method within your template as page.random_case - bear in mind that a new random choice will be made on each call, so you probably want something like {% with page.random_case as case %}.

Showing random objects in Django template

In my Django template as I am iterating through a list of objects, I'd like to have one list item say:
<li>Blah</li>
and then another do:
<li>Blah</li>
I see value|random as an option but for some reason this doesn't work:
{% ifequal [1, 2]|random 1 %}
{{ post.title }}
{% else %}
{{ post.title }}
{% endifequal %}
Doing this throws this error:
u'ifequal' takes two arguments
Is there any way to accomplish this? I would think it should be simple but I realize the Django templating language doesn't allow for variable assignments.
Thanks!
You can't put a list directly into the template like that, make_list is what you're after.
make_list returns a list of strings, so this would work.
{% if 12|make_list|random == '1' %}
<li>Blah</li>
{% else %}
<li>Blah</li>
{% endif %}

Get list item dynamically in django templates

I have some loop on the page and need list item depending from loop number.
When I call:
{{ mylist.1 }}
{{ mylist.2 }}
{{ mylist.3 }}
all works fine but what I really need is something like:
{% for x in somenumber|MyCustomRangeTag %}
{{ mylist.x }}
{% endfor %}
MyCustomRangeTag gives me Python range() it works and I already have x as number. So x is 1, 2, 3 etc. depending from loop number.
Is this possible and how?
This is not possible directly because Django thinks that "x" is the key to lookup in mylist - instead of the value of x. So, when x = 5, Django tries to look up mylist["x"] instead of mylist[5].
Use the following filter as workaround:
#register.filter
def lookup(d, key):
return d[key]
and use it like
{{ mylist|lookup:x }}
The slice tag in Django templates may use python's slicing code, but the syntax is unmistakably different. For instance, if you wanted to get an element of a sequence with a variable, in python you'd write something similar to the following:
>>>mylist = ["0th Element", "1th Element"]
>>>zero, one = 0, 1
>>>mylist[zero]
"0th Element"
>>>mylist[one]
"1th Element"
Using this syntax with the Django slice template tag will return a sliced list in every case, of dubious utility for getting an item of known index:
{% with "0" as zero %}
{% with "1" as one %}
{% with "2" as two %}
{{mylist|slice:zero}} {{mylist|slice:one}} {{mylist|slice:two}}
{% endwith %}
{% endwith %}
{% endwith %}
Renders to the html:
[] ["0th Element"] ["0th Element", "1th Element"]
Note the differences: you are getting the result of mylist[:x] instead of mylist[x].
Django provides enough tools to work around this. The first trick is to use explicit slices like 0:1 for your indices, and then |join:"" the resultant list into a single element. Like so:
{% with "0:1" as zero %}
{{mylist|slice:zero|join:""}}
{% endwith %}
Yields:
0th Element
This comes in particularly handy if you need to access a parent loop's index when dealing with an iterable inside a child loop:
{% for parent in parent_loop %}
{% cycle "0:1" "1:2" "2:3" as parent_loop_index silent %}
{% for child in child_loop %}
{{child|slice:parent_loop_index|join:""}}
{% endfor %}
{% endfor %}
Completed with nothing but stock parts, although I don't think Django has implemented achievements yet.
I notice that #e-satis mentioned it, but I think the built-in slice template tag deserves some love.
{{ item | slice:"2" }} #gets the third element of the list
Are you sure you can't just do:
{% for item in mylist %}
{{ item }}
{% endfor %}
With the slice filter, you can even do some customisation.
Following worked for me
{% for 1,2,3 in mylist %}
# do stuff
Just don't use brackets around 1,2,3

Django template for loop. Member before

I want to create such loop:
{% for object in objects %}
{% if object.before != object %}
{{ object }} this is different
{% else %}
{{ object }} this is the same
{% endfor %}
Based on https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for I can't. Is there really no simple way to do this? Or I just need to use counter and check for objects[counter-1]?
P.S. .before is theoretical and objects is simple query list. I want to take and do something with the loop member that encountered before current loop member.
Check ifchanged template tag
There is a "simple way" to do this: write a custom template tag. They're really not hard. This would probably do the trick (untested):
#register.simple_tag
def compare_objects(object_list):
comparisons = []
for i in range(1, len(object_list)):
if object_list[i] > object_list[i-1]:
comparisons.append('bigger')
else:
comparisons.append('smaller')
return comparisons
The built-in template tags and filters don't make it easy (as of Django 1.4), but it is possible by using the with tag to cache variables and the add, slugify, and slice filters to generate a new list with only one member.
The following example creates a new list whose sole member is the previous member of the forloop:
{% for item in list %}
{% if not forloop.first %}
{% with forloop.counter0|add:"-1" as previous %}
{% with previous|slugify|add:":"|add:previous as subset %}
{% with list|slice:subset as sublist %}
<p>Current item: {{ item }}</p>
<p>Previous item: {{ sublist.0 }}</p>
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}
{% endfor %}
This isn't an elegant solution, but the django template system has two faults that make this hack unavoidable for those who don't what to write custom tags:
Django template syntax does not allow nested curly parenthesis. Otherwise, we could do this:
{{ list.{{ forloop.counter|add:-1 }} }}
The lookup operator does not accept values stored using with (and perhaps for good reason)
{% with forloop.counter|add:-1 as index %}
{{ list.index }}
{% endwith %}
This code should work just fine as a django template, as long as object has a property or no-argument method called before, and objects is iterable (and '<' is defined).
{% for object in objects %}
{% if object.before < object %}
this is bigger
{% else %}
this is smaller
{% endfor %}