I am running through this wired issue. I hope I can describe it well.
The case:
Suppose that I am having the following types or identifiers:
classes = ['classa','classb','classc','classd']
and I am also having for each of the above class, these two subclasses:
subclasses = ['suba','subb']
Now, I am going to retrieve data points from the databased filtered on those classes and sub classes using a function in my model name it get_data_filtered(a,b) as follows:
context['custom-data'] = (
{c: {subc: self.get_object().get_data_filtered(c,subc)} for c in
classes for subc
in subclasses})
When printing this context to the console on the view level, everything is fine. However, when passing it to the template and accessing it there, no data is retrieved!
When viewing the template using Django Debug Toolbar, the template context shows this:
context['custom-data']:'<<triggers database query>>'
The syntax in the template level is similar to:
{% for i in custom-data.classa.suba %}
....
{% endfor %}
My analisis
I think the queryset never got evaluated this way. I also tried to put .all() and list(...) but getting nothing useful!
Can the queryset got evaluated this way?
If there is a better way to achieve such task please advice.
Django version: 2.2
I figured out that I was not accessing the context in the template level properly. The access shall be by keys just as follows:
{% for c,subcdict in custom-data.items %}
{% for subckey,objlist in subcdict.items %}
{% for obj in objlist %}
<p>{{ obj.prop }}</p>
{% endfor %}
{% endfor %}
{% endfor %}
Thanks for #moh369 observations in the post comments.
Related
I have a list with IP Addresses. I also have a nested dictionary which uses those addresses as keys. For example:
my_list = ['1.2.3.4', '8.8.8.8']
my_dic = {'data': {'1.2.3.4': 'My First Data', '8.8.8.8': 'My Second Data'}}
In my template I am trying to this:
for each in my_list:
print(my_dic['data'][each])
Everything works well until I hit that each key. So if I just print my_dic or my_dic[data] it will all work correctly. But when I add the each index nothing shows up in the webpage
I have printed each independently so I know that it works correctly.
This also works as expected when I run the loop in my IDE so I know the logic is right.
In django my code looks like this:
{% if my_dic.data %}
{% for each in my_list %}
<p>{{ my_dic.data.each }}</p>
{% endfor %}
{% endif %}
I'm new to Django so not sure if I'm overlooking something silly. Anyone know what I am missing here?
EDIT
Found a duplicate HERE which covers my issue. Same issue Vishnu Ks pointed out.
The issue is that when you try to access data.each Django will look for data["each"] and not data[each].Since there is no key named "each" in my_dic nothing would be printed.
If you want to access the value of data[each] inside the template you need to write a custom template filter.
from django.template.defaulttags import register
#register.filter
def get_item(dictionary, key):
return dictionary.get(key)
Then change the Django template code to this.
{% if my_dic.data %}
{% for each in my_list %}
<p>{{ my_dic.data|get_item:each }}</p>
{% endfor %}
{% endif %}
See Django template how to look up a dictionary value with a variable for more details.
Currently working on a e-commerce project (with django-oscar), I have an issue regarding products display on the basket template.
I use the {% regroup %} tag because I have several types of products : standalone, parent or children. Each basket line corresponds to a product, and if several of them are children sharing the same parent product, I want them to be regrouped under their common parent. However, I want standalone products to stand on their own.
My queryset is the following :
in_stock_lines = request.basket \
.all_lines() \
.order_by('product__parent', 'date_created') \
.filter(attached_to=None, product__product_class__name='Produit standard', is_customized=False)
In basket.html:
{% regroup in_stock_lines by product.parent as parent_list %}
{% for parent in parent_list %}
{% if parent.grouper %}
{% include "basket/partials/_basket_non_customized_product.html" %}
{% else %}
{% include "basket/partials/_basket_non_customized_standalone_product.html" %}
{% endif %}
{% endfor %}
The thing is that I don't want the regroup to act in the {% else %} part, because they are standalone products and are not supposed to be regrouped. But as their product.parent, i.e. the grouper is None, they are.
Is there way to prevent the {% regroup %} to act for a certain grouper value ? I could make two distinct queries in my views.py to separate standalone products from the others, and not include their template in the {% regroup %}, but I would like to avoid to make two queries if it can be.
Any help appreciated ! This is my first question on Stackoverflow, sorry if I miss some basic rules about the way I ask it.
I don't think that is something you can do in the template directly. Django deliberately limits the 'tools' available in its template language to discourage putting too much logic in the templates.
Whilst you could do the work in views.py, as what you're doing is quite presentational, I would suggest a custom template tag (more specifically an inclusion tag) is probably what you want.
You should be able to avoid multiple queries as the grouping logic is quite simple. I'd suggest looking at collections.defaultdict for simple grouping.
Let's make this very easy for my fellow SOians(?).
This is how normally the custom template tags work -
Template ->
{% block content %}
blah blah blah
{% custom_tag_load %}
{% endblock %}
The custom_tag_load is called and it returns a string. What I want to return is a queryset which I could possibly use like this ->
{% block content %}
blah blah blah
{% for x in custom_tag_load %}
{{ x.datetime }}
{% endfor %}
{% endblock %}
Note -> What I'm basically trying to do is to avoid passing the queryset through the view, and I'm not sure if I should be comfortable storing querysets in my global context.
You can return anything you like from a tag, including a queryset. However, you can't use a tag inside the for tag - you can only use a variable there (or a variable passed through a filter). What you could do is get your tag to put the queryset into a variable in the context, and use that variable in the for loop. See the docs on how to set a variable from a tag - although note that the development version has an easier method for doing this.
However, you shouldn't be concerned about putting a queryset into a context processor, either. Don't forget that querysets are lazy, so no database hit will be made unless the queryset is evaluated or iterated in the template.
A template tag can do whatever you want. From your pseudo code, you could accomplish what you need with an inclusion tag:
#my_tags.py
from django import template
from my_app.models import MyModel
register = template.Library()
#register.inclusion_tag('my_template.html')
def my_custom_tag():
things = MyModel.objects.all()
return {'things' : things}
#my_template.html
{% if things %}
<ul>
{% for thing in things %}
<li>{{ thing }}</li>
{% empty %}
<li>Sorry, no things yet.</li>
{% endfor %}
</ul>
{% endif %}
#the_view.html
{% load my_tags %}
{% my_custom_tag %}
Alternatively, you could write a custom tag that adds a queryset to the context. Hope that helps you out.
I had these same problems recently and most of the answers here were kind of outdated, but a little digging through Django's documentation and I was able to sort it out.
Like most of the answers above, you can return basically anything using a template tag, but it all depends on how you register the template tags. So say you want to use a template tag to return a queryset to be available for any template you wish, you could register the template tag as a simple tag just like this
from django import template
from blog.models import Post
from django.template.defaulttags import register
register = template.Library()
#register.simple_tag
def get_posts():
return Post.objects.all()
Then to be able to access this in your template, you first need to load this file in your template like
{% load templatetagfile %}
And then to loop through, you need to first assign it to a variable before looping through
{% get_posts as posts %}
{% for post in posts %}
{{ post.whatever }}
{% endfor %}
The first line there makes the queryset from the get_posts function available as a variable named posts which you can then loop through.
I'm using django-haystack for a search page on my site, and I want to order all the results by their content type. Is there a way I can do that?
To make it simpler, suppose I have a single application and several classes.
Thanks in advance
Not sure what you mean by content type, but if you are talking about group by models, I have this working
{% with page.object_list as results %}
{% regroup results|dictsort:"verbose_name_plural" by verbose_name_plural as grouped_objects %}
{% for ct in grouped_objects %}
{{ ct.grouper }}
{% for result in ct.list %}
<p>
{{ result.object }}
</p>
{% endfor %}
{% empty %}
<p>No results found.</p>
{% endfor %}
{% endwith %}
from How to order search results by Model:
You can do
SearchQuerySet().order_by('django_ct').
As a warning, this throws out
relevancy. The only way to keep
relevancy & group by model is either
to run many queries (one per model -
usually performant thanks to query
caching) or to run the query and
post-process the results, regrouping
them as you go.
from searchindex api:
Haystack reserves the following field
names for internal use: id, django_ct,
django_id & content. The name & type
names used to be reserved but no
longer are.
You can override these field names
using the HAYSTACK_ID_FIELD,
HAYSTACK_DJANGO_CT_FIELD &
HAYSTACK_DJANGO_ID_FIELD if needed.
If I have a list of objects that require similar layouts but need some attribute set based on the class of object how can I go about getting the class name while class and other xx values are not available within templates.
{% for obj in objects %}
<div class={{obj.__class__.__name__}}
..
</div>
{% endfor }}
There is probably an alternative approach i'm missing here..
You can also write a custom filter. My use case was to check whether or not an html element in a Django form was a checkbox. This code has been tested with Django 1.4.
I followed the instructions about Custom Filters. My filter code looks as such.
In myapp/templatetags/class_tag.py:
from django import template
register = template.Library()
#register.filter(name='get_class')
def get_class(value):
return value.__class__.__name__
In your template file:
{% load class_tag %}
{% if Object|get_class == 'AClassName' %}
do something
{% endif %}
{{ Object|get_class }}
A little dirty solution
If objects is a QuerySet that belong to a model, you can add a custom method to your model.
class mymodel(models.Model):
foo = models........
def get_cname(self):
class_name = ....
return class_name
then in your template you can try:
{% for obj in objects %}
<div class="{{obj.get_cname}}">
..
</div>
{% endfor }}
a bit simpler; assuming your layout is a list of a single model:
class ObjectListView(ListView):
model = Person
template_name = 'object_list.html'
def model_name(self):
return self.model._meta.verbose_name
Then in object_list.html:
{% for obj in object_list %}
<div class="{{view.model_name}}">...</div>
{% endfor }}
David's suggestion of a custom filter is great if you need this for several classes.
The Django docs neglect to mention that the dev server won't detect new templatetags automatically, however, so until I restarted it by hand I was stuck with a TemplateSyntaxError class_tag is not a registered tag library.
.