How to make a for loop break on counter in Django Templates? - django

How can I make the for product in products loop break after the if condition is fulfilled 3 times. I have been trying to set up a counter but that isn't working... because set is not accepted inside of for loops. Though testing it out a bit more it isn't being accepted anywhere.
When I use set it throws this exception or a variation of it: Invalid block tag on line 11: 'set', expected 'endblock'. Did you forget to register or load this tag?
I am aware that I should put all the logic I'm using Templates for inside of the view and then pass it through the dictionary but it would take too much time to research everything about that and i just want to be done with this.
Honestly I am really tired it is 6am and idk what to do. Thank you for your help in advance. :)
Edit: I know I have to use namespace() for set to be able to propagate across scopes. But set itself is still raising the same exception as above.
Edit2: I thought that django uses jinja2 as it's templating language but it seems like that is not the case. I fixed the mentions of jinja2 as I haven't changed any of the defaults that come with a fresh install of django.
HTML
{% for category in categories %}
<div>
<h5 class="text-line"><span>{{category.name}}</span></h5>
</div>
<!-- Cards Container -->
<div class="shop-cards">
{% set counter = 0 %}
{% for product in products %}
{% if product.category.categoryID == category.categoryID %}
<!-- CARD 1 -->
<div class="card">
<image class="product-image" src="{% static 'images/product-4.png' %}"></image>
<div class="card-category-price">
<h3 class="product-name">{{product.name}}</h3>
<h3 class="product-price">{{product.price}}</h3>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}

You can't (by design).Django is opinionated by design, and the template language is intended for display and not for logic.
You can use Jinja instead.
Or, you can do the complicated stuff in Python and feed the results to the template language through the context. Bear in mind that appending arbitrary objects to a list in Python is a cheap operation. So (in a CBV) something like
context['first_three'] = self.get_first_three( whatever)
...
def get_first_three(self, whatever):
first_three = []
for obj in ...:
if complicated stuff ...
first_three.append( obj)
if len(first_three) == 3:
break
return first_three
BTW you can't (easily?) implement break in Django templating, but you can easily give it the ability to count:
class Counter( object):
def __init__(self):
self.n = 0
def value(self):
return self.n
def incr(self):
self.n += 1
return ''
In the context pass context['counter'] = Counter()
In the template refer to {{counter.value}} as much as you want, and in a conditional where you want to count occurences, use {{counter.incr}}. But, it's hacky, and probably best avoided.

The problem seems to arise due to a misconception of the templating functionality. It is to be used mainly for display purposes and not filtering purposes, and you are facing a filtering problem:
I want to filter the 3 first elements of a list that fulfill a specific condition
is your main issue and templating frameworks will be poor at solving your issue, whereas this is exactly what python and Django's ORM are good at.
Why don't you first filter in Django, then display in template? For example as follows:
...
products = queryset.filter(abc=condition)[:3]
context = Context({ 'products': products })
return HttpResponse(template.render(context))

Related

Django: For Loop in Views.py?

I want to keep my templates free of any logic - just html. I'm using a "for" loop in index.html to cycle through a list of posts. Here is the loop:
{% for recent in latest %}
<h1>{{ recent.title }}</h1>
<h2>{{ recent.category }}</h2>
{% endfor %}
What I want to do is in my Posts class, grab all the posts, checking that they match certain criterion, then place them in variables which could be sent to the template.
Here is the view (the logic of which I want to eventually move to models.py):
def index(request):
# Get latest five posts
latest_posts = Post.objects.order_by('-published_date')[:5]
# Get a single "top" category post.
top_post = Post.objects.get(category = 1)[:1]
# set up some contexts
top = {'front_post': top_post}
context = {'latest': latest_posts}
return render(request, 'home/index.html', context, top)
Any suggestions?
You're taking the idea of keeping templates free of logic too far. There's a reason template languages provide things like for loops and boolean logic: without them you will wind up breaking the separation of concerns that an MVC framework is trying to enforce. Because what you will wind up with is embedding presentation information in your view logic and that's not what you want. As a simplistic example, consider adding the option of viewing your posts as a text file without adding a second view. If you keep the markup in the templates, it's easy. If you don't, it's not possible.

Django - the effect on page load speed of using include tag inside for loop

The odds are that this question will be banned, because this forum seems to me a site for "why it doesn't work"-type of questions, rather than "is it a good idea to do what I do" ones. And yet, I am very much concerned about preserving DRY-ness in my code.
I have a Django template which looks like this:
<ol id = 'task_list'>
{% for item in qs %}
{% include 'list_item.html' with item=item %}
{% endfor %}
</ol>
list_item.html:
<li>
{{item}}
</li>
The advantage (at least, for me) of this code: it positively affects DRYness when I have a ajax code which posts a request for creating new items of the list and renders them on the client side subsequently:
JS:
$.post('my_view_url', function(response)
$('#container').append(response);
Django view:
def my_view(response)
#...
return render_to_response('list_item.html',....)
This way, list_item.html helps me use the same HTML code for both initial rendering of existing elements and client-side rendering of newly created items.
The disadvantage is that {% include %} is known to be rather slow.
The question: Is this code pattern not a performance killer in case of paginated rendering of large arrays of data ?
Additional note:
AFAIK, {% block %} is faster than {% include %}. But I've got no idea how to rewrite the code pattern using block.

Django template variable containing template tag, ex {{ {% some_tag %} }}

I have a template that receives a list context variable, tags_list. I need to iterate over this list 'inserting' the tags in the template something like this:
{% for tag in tags_list %}
{{ tag.tag }}
{% endfor %}
When this renders it returns the text value of tag.tag, "{% tagxxx %}", not the rendered tag.
How can I cause the template render to render the value of a context variable? Alternately, is there a filter, a sort of inverse verbatim, that will cause the value of a context variable to be rendered?
Updated background
tags_list is created by a fairly sophisticated process involving exec of some user provided text from a table/model field. The relevant portion of the real template looks like this:
{% for graph_row in graph_rows %}
<div class="row">
{% for graph in graph_row %}
<div class="col-md-{{ graph.width }}">
{{ graph.graph }}
</div>
{% endfor %}
</div>
{% endfor %}
The graph values look like this: {'graph':'{% piechart data1 %}', 'width':3}
Note that the order of entries in the context variable graph_rows is significant as is order of graph(s) in the row as that determines the placement of graphs on the page. Preserving this order is essential for the scheme to work correctly.
Currently, the view function simply does an {% include ... %} to get the template segment above to render in the correct order. This approach is simple and clean.
I could, as has been suggested, perform a template render within the view function but that complicates the design a bit and I'd hoped to avoid doing that if there is an easy way to trigger a render of {{ graph.graph }}. Note, as well, by moving the render into the view I loose the ability to easily take the template from arbitrary places, in particular table fields.
One of the great things about Django is the library of solution and code snippets. Sadly, they aren't a well organized and easy to find as one might wish. Nevertheless, a bit of google found a number of solutions of the general form
{% render tag.tag %}
Here are links to several:
render_as_template template tag
Allow template tags in a Flatpage's content
render_as_template.py
I'll use the general approach cleaned up a bit for error checking.
As an aside, the technique strikes me as generally useful and might be appropriate for inclusion in the standard tags.
Update 3/28/2014
After looking at the above and several others this is what I used from render_as_template template tag. There is a useful comment here.
from django import template
from django.template import Template, Variable, TemplateSyntaxError
register = template.Library()
class RenderAsTemplateNode(template.Node):
def __init__(self, item_to_be_rendered):
self.item_to_be_rendered = Variable(item_to_be_rendered)
def render(self, context):
try:
actual_item = self.item_to_be_rendered.resolve(context)
return Template(actual_item).render(context)
except template.VariableDoesNotExist:
return ''
def render_as_template(parser, token):
bits = token.split_contents()
if len(bits) !=2:
raise TemplateSyntaxError("'%s' takes only one argument"
" (a variable representing a template to render)" % bits[0])
return RenderAsTemplateNode(bits[1])
render_as_template = register.tag(render_as_template)
This gets part of the way to a solution. Unfortunately custom template tags, in my case
{% pie_chart %} are not available to render within the class RenderAsTemplateNode.
I've not tested this but it appears that this stack overflow question, Django - replacing built-in templatetag by custom tag for a whole site without {% load .. %}, points the way.
I believe I can provide a way for you to get the results you want, but there might be a better way for you to achieve the desired functionality if you can provide some context.
Anyway, you might do something like this in your view.py:
tags_list = [
Template('{% load my_tags %}{% ' + t.tag + ' %}').render(Context())
for t in tags_list
]

Using the Django assignment_tag built-in tag the right way

I'm working on a project in Django.
Earlier today, I discovered the new (Django >= 1.4) assignment_tag. I immediately decided that it was just what I needed EVERYWHERE and threw some logic into one that executed a very simple query against the database and returned the resulting queryset. The function I wrapped takes an argument that allows the invoking context to specify how many results to grab, directly in the template when I am using the template tag.
It is quite convenient - I don't have to update my view when I decide this list should have 5 items, not 3 - but it seems like one of those gray areas where we aren't supposed to tread (i.e. pushing application logic into templates) when writing good, maintainable Django code.
Now, a couple of hours separated from writing the code, I'm wondering if I should scrap the assignment_tag entirely.
Code:
models.py:
class SomeObject(models.Model):
is_active = models.BooleanField(default=False)
(...)
templatetags/myapp_tags.py:
from django import template
from myapp.models import SomeObject
register = template.Library()
#register.assignment_tag
def get_someobjects_list(max_results=0):
queryset = SomeObject.objects.filter(is_active=True)
if max_results == 0:
return queryset
elif max_results > 0:
return queryset[:min(max_results, queryset.count())]
else:
return None
templates/myapp/chunks/someobject_list.html:
{% load myapp_tags %}
{% get_someobjects_list as someobjects_list %}
# or {% get_some_objects_list 5 as someobjects_list %} ... flexible!
{% if someobjects_list %}
<ul>
{% for someobject in someobjects_list %}
<li>
<a href="{{ someobject.get_absolute_url }}">
{{ someobject.name }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<span>No someobjects exist</span>
{% endif %}
I was really excited to discover these existed - it's convenient for me in this particular case. Now that my excitement over finding a new feature has passed, it seems pretty clear that I'm misusing it. The example given in the Django docs seems like a better application of this - grabbing the string representation of current datetime, something that doesn't require a DB query. My worry is that I'm setting myself up for heartache if I start using this pattern regularly. Following the slippery slope all the way down: I'll end up not even bothering to pass a context to my templates and ALL my DB queries will be hidden away in template tags where nobody would think to look for them.
It seems the code would be cleaner if I just threw out this whole "great idea" I had when I discovered assignment_tags and created a custom model manager instead.
Are there other clean ways of accomplishing this that I am missing? Are manager methods the consensus best way among Django developers?
assignment template tags are especially helpful if you need to get some information into the template context for a few pages of a website, but don't want to (or can't) put the info into every view on the website, and don't want to or can't rely on a context processor.
they basically guarantee that your information will be available in the template.

Django: How do I get the number of elements returned in a database call?

This seems to me like a very simple question, but I can't seem to find the answer.
All I need to do is determine the number of objects returned by a database query.
The specific circumstance is this: I have a model named Student. This model has a ManyToManyField member named courses_current, which relates to a table of Course models. When I pass my Student instance to a template, I want to be able to do something like the following (the syntax may not be exact, but you'll get the basic idea):
<div id="classes">
{% if student.classes_current.all.size == 0 %}
<h1> HEY! YOU AREN'T TAKING ANY CLASSES! REGISTER NOW!
{% else %}
Here are your courses:
<!-- ... -->
{% endif %}
</div>
Now, I'm fairly certain that X_set.all.size is not a real thing. In the manage.py shell I can just use len(student.classes_current.all()), but I don't know of any way to use built-in functions, and "dictionary-like objects" don't have .size() functions, so I'm at a loss. I'm sure there's a very simple solution (or at least I hope there is), but I can't seem to find it.
{{ student.classes_current.all.count }} but be warned that it doesn't fetch the objects so you will need to do a separate query if you want to loop over them.
If you need loop over the classes for tag has way to get what you need.
{% for cl in student.current_classes.all %}
{{ cl }}
{% empty %}
<h1>Hey! ...</h1>
{% endfor %}
Documentation https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for-empty