Indexing a QuerySet array in django templates - python

I'm trying to access a queryset array that I passed from the views in the templates. I want to index each entry using a numeric iterator. I'm using a django snippet to get the range of customers. Here is what I have done so far:
{% for cust in customer_comments %}
{% for i in cust|length|get_range %}
<tr>
<td>{{cust.i.customer_id}}</td>
<td>{{cust.i.feedback_detail}}</td>
</tr>
{% endfor %}
{% endfor %}
When I iterate using cust.i.customer_id it displays nothing. But when I use cust.0.customer_id or cust.1.customer_id, it displays what I want it to. Kindly help why i is not working.
Btw this is how I initialized the customer_comments object in views.
customer_comments = []
for i in all_features:
if OpenFeedback.objects.filter(feature_id = i.feature_id).exists():
feedback_obj = OpenFeedback.objects.filter(feature_id = i.feature_id)
customer_comments.append(feedback_obj)

You don't iterate like that in Python or in Django templates: you iterate through the list itself.
{% for customer in cust %}
<tr>
<td>{{customer.customer_id}}</td>
<td>{{customer.feedback_detail}}</td>
</tr>
{% endfor %}

Related

django "<QuerySet{[ ]}" appearing in list

I have been wrestling with something that I think is a bonehead oversight on my part. I have a form that feeds an input to a view that queries some SQL tables I have and returns a list back with columns from each table**.
The odd thing that is happening is the my list is appearing with
<QuerySet{[ ]}>
brackets around each list object. Can anyone tell me how to avoid this?
Much appreciated.
**I am using this list to combine these tables rather than ForeignKeys because I was having a terrible time getting my SQL databases to populate correctly using SQLAlchemy and Postgres and read that there were known issues with that, so I gave up on that method.
views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.views import generic
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import *
from .forms import QuoteForm, RfqForm
def bom_result(request):
if request.method == 'POST':
form = RfqForm(request.POST)
if form.is_valid():
bom_list = []
rfq = {}
rfq_search = form.cleaned_data['rfq_entered']
rfq['rfq_num'] = rfq_search
rfq['bom'] = Quotereq.objects.values('bom_entered').filter(rfq_num__exact=rfq_search)
rfq['part_num'] = Bom.objects.values('partnum').filter(bom__exact='07-00-000019')
bom_list.append(rfq)
context = {'bom_list': bom_list}
return render(request, 'quote/result.html', context)
else:
return HttpResponse("<h1>Something Went Wrong</h1>")
else:
form = RfqForm()
context = {'form': form}
return render(request, 'quote/lookup.html', context)
result.html
{% extends "base.html" %}
{% load static %}
{% block title %}{{title}}{% endblock title %}
{% block sidenav %}
{% for page in page_list %}
<li>
{{page.title}}
</li>
{% endfor %}
{% endblock sidenav %}
{% block content %}
{% autoescape off %}
{{ content }}
{% endautoescape %}
{% if bom_list %}
{% for bom in bom_list %}
<table>
<tr>
<th><h1>RFQ Number</h1></th>
<th><h1>BOM</h1></th>
</tr>
<tr>
<td>
<ul style="list-style-type:none">
<li>{{ bom.rfq_num }}</li>
</ul>
</td>
<td>
<ul style="list-style-type:none">
<li>{{ bom.bom }}</li>
</ul>
</td>
<td>
<ul style="list-style-type:none">
<li>{{ bom.part_num }}</li>
</ul>
</td>
</tr>
</table>
{% endfor %}
{% else %}
<p>No Matching RFQ in Database</p>
{% endif %}
{% endblock content %}
Here is what the table outputs:
If we look at what part_num is, we see:
rfq['part_num'] = Bom.objects.values('partnum').filter(bom__exact='07-00-000019')
This is a QuerySet, a QuerySet is some sort of query that results in a set of objects. You construct such QuerySets by using the Django ORM (what you do here).
In order to obtain the elements in the QuerySet, you can iterate over the elements, and for example individually process these.
In the template we can for example write:
<td>
<ul style="list-style-type:none">
<li>{% for part in bom.part_num %} {{ part.part_num }} {% endfor %}</li>
</ul>
</td>
So we iterate over the QuerySet, we then each time obtain a dictionary (that was wrapped in the QuerySet, and we render the value that corresponds to the 'part_num' key in the dictionary. Of course we can make it more sophisticated (for example use HTML markup directives, like {% for part in bom.part_num %} <b>{{ part.part_num }}</b> {% endfor %} to put it in boldface.
I am using this list to combine these tables rather than ForeignKeys because I was having a terrible time getting my SQL databases to populate correctly using SQLAlchemy and Postgres and read that there were known issues with that, so I gave up on that method.
I would really advice to use ForeignKeys, ManyToManyFields, etc. to represent relations between entities. Not only will Django allow you to make more expressive queries, it also will add constraints to the database such that columns that correspond to a ForeignKey will always point to the primary key of the table where they point at, etc. In case you for example remove a referred entity, you can define triggers what to do (remove related objects, set the foreign key columnn to NULL, etc.). Perhaps following the Django tutorial can provide some required information to proceed.
Of course you are free to design and implement the project in the way you like, but my personal experience is that it will probably in the end result in more trouble.

Using the slice filter with context data from a Django QuerySet

I am trying to split a list from my model across two columns, using this html code in the template:
< div class ="col-md-6" >
{%for value in object_list %}
<ul>< ahref="/sites/{{value.url}}/">{{value.Site}}</a></ul>
{% endfor %}
I was planning to achieve this with the slice tag to filter the list, e.g.:
{%for value in object_list|slice:"10:20" %}
It does not work however, and I think it might be because I have context data i.e. {{value.Site}}, instead of just {{Site}} for example. This is the corresponding view:
class homeview(ListView):
template_name = 'annual_means/home.html'
def get_queryset(self):
return AnnualMean.objects.values("Site", "url").distinct()
What do I need to do to get the slice to work?
I think, what you need is this:
<table>
<tr>
<th>URL</th>
<th>SITE</th>
</tr>
{% for value in object_list %}
<tr>
<td>{{value.url}}</td>
<td>{{value.Site}}</td>
</tr>
{% endfor %}
</table>
URLs and Sites will be displayed as a table.

Iterating over related objects in Django: loop over query set or use one-liner select_related (or prefetch_related)

I have a newsletter application where a newsletter has multiple articles within each issue. I want to display a summary page online that lists the newsletter year, volume and label, and then in an unordered list display all the articles in the issue. I am quite new to Django so I am trying to determine the best way to do this.
I have the models defined (just the relevant parts):
Models.py:
class Newsletter(models.Model):
volume = models.ForeignKey(Volume)
year = models.IntegerField()
season = models.CharField(max_length=6, choices=VOLUME_SEASON)
label = models.CharField(max_length=20)
number = models.IntegerField()
class Article(models.Model):
newsletter = models.ForeignKey(Newsletter)
section = models.ForeignKey(Section)
title = models.CharField(max_length=200)
What I want to see on the web looks like:
<h2>Spring 2012</h2>
<p>Volume 14, Number 1</p>
<ul>
<li>Foo</li>
<li>Bar</li>
<li>Baz</li>
</ul>
<h2>Winter 2011</h2>
<p>Volume 13, Number 4</p>
<ul>
<li>Boffo</li>
</ul>
Pretty simple. However, I am confused by the best way to write my view. Whether to use:
Two lists which I zip() and then iterate over in the template
Use the select_related() queryset
Use the prefetch_related() queryset
I have it working using the first option:
Views.py:
from django.shortcuts import render_to_response, get_object_or_404
from www.apps.newsletter.models import Newsletter, Article
def index(request):
article_group = []
newsletter = Newsletter.objects.all().order_by('-year', '-number')
for n in newsletter:
article_group.append(n.article_set.all())
articles_per_newsletter = zip(newsletter, article_group)
return render_to_response('newsletter/newsletter_list.html',
{'newsletter_list': articles_per_newsletter})
And then render it using the following template:
Newsletter_list.html:
{% block content %}
{% for newsletter, articles in newsletter_list %}
<h2>{{ newsletter.label }}</h2>
<p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
<p>{{ newsletter.article }}</p>
<ul>
{% for a in articles %}
<li>{{ a.title }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}
Pretty straightforward, but as I am pretty new to Django I was wondering if what I am doing is completely inefficient in terms of its powerful ORM. I would love to not have to make a list on-the-fly and then zip() the two lists together if there is a faster way.
TIA.
The approach you are doing now will be heavily inefficient, because it will result in an 1+N number of queries. That is, 1 for the query of all your Newsletters, and then 1 for every single time you evaluate those n.article_set.all() results. So if you have 100 Newletter objects in that first query, you will be doing 101 queries.
This is an excellent reason to use prefetch_related. It will only result in 2 queries. One to get the Newsletters, and 1 to batch get the related Articles. Though you are still perfectly able to keep doing the zip to organize them, they will already be cached, so really you can just pass the query directly to the template and loop on that. :
view
newsletters = Newsletter.objects.prefetch_related('article_set').all()\
.order_by('-year', '-number')
return render_to_response('newsletter/newsletter_list.html',
{'newsletter_list': newsletters})
template
{% block content %}
{% for newsletter in newsletter_list %}
<h2>{{ newsletter.label }}</h2>
<p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
<p>{{ newsletter.article }}</p>
<ul>
{% for a in newsletter.article_set.all %}
<li>{{ a.title }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}

Define variables in template based on user being staff or not

I'm trying to display an HTML table of values with about 20 columns where say staff users see one subset of columns, and non-staff users see another subset of columns. I may want to define further types of users later. Now right now I have three static header rows so the template looks like
<table>
<tr>
<th>Col A</th>
{% if user.is_staff %}<th>Col B</th>{% endif %}
...
{% if not user.is_staff %}<th>Col K</th>{% endif %}
</tr>
<tr>
<td>Col A second header</td>
{% if user.is_staff %}<td>Col B second header</td>{% endif %}
...
{% if not user.is_staff %}<td>Col K second header</td>{% endif %}</tr>
<tr><td>Col A third header</td> ... </tr>
{% for obj in object_list %}
<tr>
<td>{{ obj.col_a }}</td>
{% if user.is_staff %}<td>{{ obj.col_b }}</td>{% endif %}
...
{% if not user.is_staff %}<td>{{ obj.col_k }}</td>{% endif %}
</tr>
{% endfor %}</table>
However, I find non-DRY as every time, if I want to change if a user-type can see a column, I have to change it in 4 places. Or if I want to define multiple different classes of users, I'd have to have complicated if statements. I'd prefer something like
{% if show_col_a %}<td>{{obj.col_a }}</td>{{% endif %}
Where I can define at the top of the template (or possibly in the view) that user.is_staff can see show_col_a. Is something like this possible? I'm using a generic view (object_list). Maybe modify all users to have attributes user.show_col_a somehow and do {% if user.show_col_a %}? I'm not sure how to add boolean attributes to users.
EDIT: May want multiple users with custom views (e.g., staff_view; admin_view, unprivileged, etc.), so if statements would get unwieldy. A cell's contents is typically more complicated than {{ obj.col_b }}; tried simplifying problem to get to the point. E.g.:
<td>{% if obj.custom_address %}
{{ obj.custom_address.webprint|safe }}
{% else %}
{{ obj.business.address.webprint|safe }}
{% endif %}</td>
Also while multiple templates would work with a simple switch like:
{% if user.is_staff %}
{% include "template_staff.html" %}
{% else %}{% if user.is_admin %}
{% include "template_admin.html" %}
{% else %}
{% include "template_other.html" %}
{% endif %}
{% endif %}
I find its not DRY at all; e.g., every edit to a template has to be replicated in three template. I guess I could make a script that read generates the three templates from some super_template outside of django but its getting very inelegant.
This depends a lot on what view you have and templates.
Ways to do:
make a public template and staff template and add a simple method to change the templates on the fly for the views.
make a template tag:
{% is_staff myvar %}
tag code:
class IsStaffNode(Node):
def __init__(self, var):
self.var = var
def render(self, context):
if context['user'].is_staff():
return var.resolve(context)
return ""
#register.tag
def is_staff(parser, token):
var = parser.compile_filter(token.split_contents()[1])
return IsStaffNode(var)
Homework: make it a block tag to include the td's so that it's shown either all or none.
{% isstaff myvar %}<td>{{ myvar }}</td>{% endisstaff %}
This way is more labor intensive than 2 different templates, but if you want to try, manipulating the context (or creating a separate context for the block only) might be useful.
Make a context processor that would fill the context with some variables if the user is staff, or not if not.
Make a tag that would include the template (inherit from IncludeNode) and manipulate the context.

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