Django: multi-dimensional dictionary displayed in template - django

I have the following view and I'm finding it impossible to display through a template:
sm = request.session.get('active_semester')
semester_dates = SemesterDates.objects.filter(semester=sm)
schedule = Schedule.objects.filter(semester=sm)
student_enrolls = StudentEnroll.objects.filter(schedule__semester = sm).values_list('schedule__pk', 'student__family_member__first_name', 'student__family_member__last_name').order_by('student__family_member__last_name')
schedule_array={}
for a in schedule:
schedule_array[a.id]={'course':a.course_catalog.course_name, 'students': [], 'teachers': []}
for (pk, fname, lname) in student_enrolls:
schedule_array[pk]['students'].append(fname + ' ' + lame)
This produces the following output. Note that there are two "Arduino" classes because they are held at different times. So, I'm using the Schedule.pk as the dictionary key to keep them separate. This way my class list is different for each instance of this course.
{2L:
{
'students': [u'Jessica Ryan', u'Annie Ryan'],
'course': u'Arduino Programming',
'teachers': []
},
4L:
{
'students': [],
'course': u'Lego Animation 3rd-4th Grade',
'teachers': []
},
5L:
{
'students': [],
'course': u'Life skills - card games',
'teachers': []
},
6L:
{
'students': [],
'course': u'test',
'teachers': []
},
7L: {
'students': [u'Mark Ryan'],
'course': u'Arduino Programming',
'teachers': []
}
}
In my template, I have the following. This is not working but gives you a glimpse of what I'm trying to do.
<table class="table table-hover">
{% for e in schedule %}
<tr class="info">
<td><h3>{{ e.course_catalog }}</h3></td>
</tr>
<tr>
<td>
{% for key in schedule_array.items|lookup:0 %}
<h5>Dict Key: {{ key }} & Value: {{ value }} {{e.pk}} </h5>
{% for k2 in key.items %}
k2: {{k2}} Students: {{k2.students}}<br>
{% for k3 in k2 %}
{% for s in k3 %}
k3:{{s.students}}
{% endfor %}
{% endfor %}
endfor 2
{% endfor %}
endfor 1
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
What I'm trying to do is loop through my schedule object. Then, I'd like to look up the primary key (e.pk) in the schedule_array dictionary. From that, I'd like to loop through the list of students and then do it again with the teachers. I'm not able to get this to work. Regardless of what I try. I know I'm doing it wrong but can't find the correct way to accomplish it.

Figured it out:
<table class="table table-hover">
{% for key, value in schedule_array.items %}
<tr class="info">
<td><h3>{{ value.course }}</h3></td>
</tr>
<tr>
<td>
{% for s in value.students %}
k2: {{s}}
{% endfor %}
</td>
<td>
{% for t in value.teachers %}
k2: {{t}}
{% endfor %}
</td>
</tr>
{% endfor %}
</table>

Related

Get Data Problem with Foreignkey Models QuerySet (Django)

I am making django practising. I found a repo and edited to myself. When i came to the get data from models with foreignkey, i get query instead of data. I think my function is a little bit spagetti or my template file.
Full repo is here https://github.com/eseymenler/demo2
Here is the output of my codes. First red square is query, but i want second red square datas.
Hasta Adı: {{ patient.name }}
Hastanın Sağlık Problemi: {{ prob }}
Hastanın Boyu: {{ vital }}
Hastanın Sigara Kullanım Durumu: {{ social }}
First data {{ patient.name }} is very good for me. Thats what i want.
But when i get write {{ prob.problem }} it gives me nothing. So where is my fault.
But also when i make for loop like this, i get data which i want. So how can i fix this.
{% for y in prob %}
{{ y.problem }}
{% endfor %}
And my views.py
#login_required()
def patienttumbilgiListView(request, id):
patient = Patient.objects.get(aadhaarId = id)
prob = ProblemList.objects.filter(patient = patient)
vital = VitalSign.objects.filter(patient = patient)
social = SocialHistory.objects.filter(patient = patient)
return render(
request, 'patient_records/patient-problem-tum.html',
context={'prob': prob, 'vital': vital, 'social': social, 'patient': patient })
and my template file
{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% load i18n %}
{% block title %}problem-list-view{% endblock title %}
{% block content %}
<div class="container">
Hasta Adı: {{ patient.name }}</br>
Hastanın Sağlık Problemi: {{ prob }}<br>
Hastanın Boyu: {{ vital }}<br>
Hastanın Sigara Kullanım Durumu: {{ social }} <br></div>
<br>
<br>
<div class="container">
<style type="text/css">
.tg {border-collapse:collapse;border-spacing:0;}
.tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
overflow:hidden;padding:10px 5px;word-break:normal;}
.tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
.tg .tg-0lax{text-align:left;vertical-align:top}
</style>
<table class="tg">
<thead>
<tr>
<th class="tg-0lax">Hasta Adı:</th>
<th class="tg-0lax">{{ patient.name }}</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-0lax">Hastanın Sağlık Problemi:</td>
<td class="tg-0lax">{% for y in prob %}<br> <br> {{ y.problem }}<br> <br> <br>{% endfor %}</td>
</tr>
<tr>
<td class="tg-0lax">Hastanın Boyu:</td>
<td class="tg-0lax">{% for x in vital %}<br> <br> {{ x.height }}<br> <br> <br>{% endfor %}</td>
</tr>
<tr>
<td class="tg-0lax">Sigara Kullanımı:</td>
<td class="tg-0lax">{% for item in social %}<br> <br><br> {{ item.tobacco_smoking_status }}<br> <br> <br>{% endfor %}</td>
</tr>
</tbody>
</table>
</div>
<br>
<br>
{% endblock %}
Roughly What You Had
So your code is:
#login_required()
def patienttumbilgiListView(request, id):
patient = Patient.objects.get(aadhaarId = id)
prob = ProblemList.objects.filter(patient = patient)
vital = VitalSign.objects.filter(patient = patient)
social = SocialHistory.objects.filter(patient = patient)
return render(
request, 'patient_records/patient-problem-tum.html',
context={'prob': prob, 'vital': vital, 'social': social, 'patient': patient })
{% for y in prob %}
{{y.problem}}
{% endfor %}
{% for x in vital %}
{{x.height}}
{% endfor %}
{% for item in social %}
{{item.tobacco_smoking_status}}
{% endfor %}
Some Friendly Suggestions
"id" is a reserved word / function - I suggest changing id to maybe pid in your code.
Return an error to your view - You don't necessarily have to make this change, but I think its a good idea, and can even help with troubleshooting. I mean you should probably be returning an error or something to this chart if you can't find these objects with those ids in your database.
"get()" throws a DoesNotExist error if the id isn't found - You should catch the error and return it to the console you are making or do something like that.
views.py
#login_required()
def patienttumbilgiListView(request, pid):
context = {}
try:
patient = Patient.objects.get(aadhaarId = pid)
except DoesNotExist:
context['error'] = f"Could not find patient with id '{pid}'."
else:
context['prob'] = ProblemList.objects.filter(patient=patient)
context['vital'] = VitalSign.objects.filter(patient=patient)
context['social'] = SocialHistory.objects.filter(patient=patient)
context['patient'] = patient
return render(request, 'patient_records/patient-problem-tum.html', context=context)
template.html
{% if error %}
{{error}}
{% endif %}
{% for y in prob %}
{{y.problem}}
{% empty %}
Could not find problems.
{% endfor %}
{% for x in vital %}
{{x.height}}
{% empty %}
Could not find vitals.
{% endfor %}
{% for item in social %}
{{item.tobacco_smoking_status}}
{% empty %}
Could not find socials.
{% endfor %}
What Your Problem Is And How To Solve It
I am making django practising. I found a repo and edited to myself. When i came to the get data from models with foreignkey, i get query instead of data.
Based on this quote, I am assuming you want to know why the data is being returned as a queryset instead of as a singular result. From the docs:
filter() will always give you a QuerySet, even if only a single object matches the query - in this case, it will be a QuerySet containing a single element. If you know there is only one object that matches your query, you can use the get() method on a Manager which returns the object directly: one_entry = Entry.objects.get(pk=1).
Which is why you should use the for loop in your template.html. If you don't want to use that, then you should preprocess this data either by using get() instead of filter() or simply, in the context, send the [0] element to the template from your view.
So, to clarify, if you want to use {{prob.problem}} in your template, you need to change your view to either do prob = ProblemList.objects.filter(patient = patient)[0] or prob = ProblemList.objects.get(patient = patient). If you choose the first, you should check the length (prob = prob[0] if len(prob) == 1 else None), otherwise, if you choose the latter, you should wrap it with a try, except, else clause like this:
try:
prob = ProblemList.objects.get(patient=patient)
except DoesNotExist:
context['error'] = "Could not find ProblemList."
print("Does Not Exist")
else:
context['prob'] = prob
or you could just use the for loop that you have in place, I don't know how many entries you are expecting the query set to have per patient.
If I need to further clarify anything, please let me know.

How to hide entire row if one or two fields are empty Django

How can I hide an entire row if one or more specific fields are empty? For example, I have a django query set up so that I can get a total profit from items in the inventory manager. The way that I have that written is like:
html
{% extends 'portal/base.html' %}
{% block title %}Inventory{% endblock %}
{% block content %}
<br>
<div class="row">
<div class="col">
<form class="d-flex" role="search" action="/search" method="get">
<input class="form-control me-2" type="text" name="q" placeholder="Search" aria-label="Search">
<button class="btn btn-outline-success">Search</button>
</form>
</div>
<div class="col">
<a class="btn btn-primary me-md-2" href="/newitem" type="button">Input New Purchase</a>
</div>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Breakdown</th>
<th>Product ID</th>
<th>Product</th>
<th>Total Profit</th>
</tr>
</thead>
{% for inventory in inventory %}
<tr>
<td><a class='btn btn-success btn-sm' href=''>View Breakdown</a>
<td>{{inventory.id}}</td>
<td>{{inventory.product}}</td>
<td>{{ inventory.Calculate_profit }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
views.py
#login_required(login_url="/login")
def profitsperitem(request):
inventory = Inventory.objects.all().order_by('id')
return render(request, 'portal/profitsperitem.html', {"inventory": inventory})
models.py
#property
def Calculate_profit(self):
soldfor = Inventory.objects.filter(soldprice=self.soldprice).aggregate(Sum('soldprice'))['soldprice__sum'] or 0.00
paidfor = Inventory.objects.filter(paid=self.paid).aggregate(Sum('paid'))['paid__sum'] or 0.00
shipfor = Inventory.objects.filter(shipcost=self.shipcost).aggregate(Sum('shipcost'))['shipcost__sum'] or 0.00
totalprofit = soldfor - paidfor - shipfor
return totalprofit
As long as the model fields soldprice , paid , and shipcost are all filled out on every row in the database, I can get the results no problem. I get an error if soldprice or shipcost are null or none, so when there is nothing added to database. If one row does not have soldprice or shipcost set, none of the results can be viewed as this error pops up: "TypeError at /profitsperitem
unsupported operand type(s) for -: 'float' and 'decimal.Decimal'"
My question is how can I hide the entire row if either or both soldprice and/or shipcost are empty?
Tho filter all rows with any of the two fields beeing null/None use a query like this
from django.db.models import Q
#login_required(login_url="/login")
def profitsperitem(request):
inventory = Inventory.objects.filter(
Q(soldprice__isnull = False) | Q(shipcost__isnull = False)
).order_by('id')
return render(request, 'portal/profitsperitem.html', {"inventory": inventory})
I was able to update the view like this
#login_required(login_url="/login")
def profitsperitem(request):
inventory = Inventory.objects.filter(soldprice__isnull = False, shipcost__isnull = False).order_by('id')
return render(request, 'portal/profitsperitem.html', {"inventory": inventory})
{% for inventory in inventory %}
{% if inventory.Calculate_profit %}
<tr>
<td><a class='btn btn-success btn-sm' href=''>View Breakdown</a>
<td>{{inventory.id}}</td>
<td>{{inventory.product}}</td>
<td>{{ inventory.Calculate_profit }}</td>
</tr>
{% endif %}
{% endfor %}
you can print row only if you have calculate price.
if(soldfor is not None and paidfor is not None and shipfor is not None )
totalprofit = soldfor - paidfor - shipfor
else totalprofit =None
Also You can check if Calculate profit has some value

Django template jinja for loop

Can someone suggest me how to iterate a dictionary in django template?
dic1={5: ['jaw replacement - 23-Jun-2020 (16:20:09.164240)', 'jaw replacement done - 23-Jun-2020 (16:20:51.158085)', 'jaw replacement done,almost done - 23-Jun-2020 (16:25:40.066955)', 'jaw replacement done,almost done 1 - 23-Jun-2020 (16:27:00.355605)', 'jaw replacement done,almost done 2 - 23-Jun-2020 (16:31:35.111660)']}
this is the context i am passing
i need something like this in template
for i in dic1:
print(i)
list1=dic1[i]
for k in list1:
print(k)
{% for i in notes %}
{% with list1=dic1[i] %}
{{list1}}
{% endwith %}
{% endfor %}
This is not working.
Lets say your data is -
dic1 = {'a': [ [1, 2] ], 'b': [ [3, 4] ],'c':[ [5,6]] }
You can use the data.items() method to get the dictionary elements. Note, in django templates we do NOT put (). Also some users mentioned values[0] does not work, if that is the case then try values.items.
<table>
<tr>
<td>a</td>
<td>b</td>
<td>c</td>
</tr>
{% for key, values in dic1.items %}
<tr>
<td>{{key}}</td>
{% for v in values[0] %}
<td>{{v}}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
I hope It helps you.
In normal Python you would normally access keys and values by using the items() method on a dictionaty.
For example given the dictionary
some_dic = {'1': 'Hello', '2': 'World'}
You should be able to access this by doing the following:
for key, value in some_dic.items():
print(key, value)
You can follow this same principle in Django templates and simply omit the '()' as follows:
{% for key, value in some_dic.items %}
<h1>Key: {{ key }}</h1>
<h1>Value: {{ value }}</h1>
{% endfor %}
you can pass dic1 one directly to the template from view and iterate through dictionary in template \
in view
return render(request, 'template.html',{'dic1':dic1})
#intemplate.html
{% for key, value in dic1 %}
<tr>
<td> Key: {{ key }} </td>
{% for value_in_array in value %}
<td> Value: {{ value }} </td>
{% endfor %}
</tr>
{% endfor %}

Using function into template (Django/Python)

I'm trying to use a function that compare the keys from headers and datas of a form. If the keys aren't similar, this function adds an empty field.
It works perfectly fine in my views.py :
#Entries define all the datas taken from the fobi forms.
headers = json.loads(entries{0}.saved_data)
headers = headers.items()
for key, value in headers:
for data in entries:
data = json.loads(data.saved_data)
formatted[value] = data.get(key, '')
print(entries)
formatted = formatted.items()
Then i pass formatted into the context and in my template i did :
<tr>
{% for key, valeur in headers %}
<th>
{{ key }}
</th>
{% endfor %}
</tr>
</thead>
<tbody>
<tr>
{% for key, valor in headers %}
{% for cle, valeur in formatted %}
{%if cle == valor%}
<td> {{valeur}}</td>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
</tbody>
The result is perfect, such as every header is aligned with the datas. And if there are some datas missing such as picture, there is an empty sace in this column.
I only printed one form through my loop ( the last saved from the formatted dict). And i want to print all my forms. But it seems impossible in the views.py.
How i manage it into the template ?
Thank you for your answers !
BR, Karro.
I found the solution.
I created a list of dictionnaries :
formattedList = []
Then, i change my initial loop in order to get every dictionnary set in the list :
for entry in entries:
formatted = {}
for key, value in headers:
data = json.loads(entry.saved_data)
formatted[value] = data.get(key, '')
formatted = formatted.items()
**formattedList.append(formatted)**
Finally, i changed these lines in my template file :
<tbody>
**{%for element in formattedList%}**
<tr>
{% for key, valor in headers %}
**{% for cle, valeur in element %}**
{%if cle == valor%}
<td> {{valeur}}</td>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
Everything worked fine !
I got all my results !

Using forloop.counter value as list index in a Django template

in my Django 1.1.1 application I've got a function in the view that returns to his template a range of numbers and a list of lists of items, for example:
...
data=[[item1 , item2, item3], [item4, item5, item6], [item7, item8, item9]]
return render_to_response('page.html', {'data':data, 'cycle':range(0,len(data)-1])
Inside the template I've got an external for loop, that contains also another for cycle to display in output the contains of the inner lists of data in this way
...
{% for page in cycle %}
...
< table >
{% for item in data.forloop.counter0 %}
< tr >< td >{{item.a}} < /td > < td > {{item.b}} ... < /td > < /tr >
...
< /table >
{% endfor %}
{% if not forloop.last %}
< div class="page_break_div" >
{% endif %}
{% endfor %}
...
But Django template engine doesn't work with the forloop.counter0 value as index for the list (instead it does if I manually put a numeric value as index). Is there a way to let the list loop works with the external forloop.counter0 value?
Thanks in advance for the help :)
I solved this in a rather inefficient way. Please don't throw up on your computer when you read this code. Given two lists of identical length, it will iterate through the first and print the corresponding item from the second.
If you must use this, only use it for rarely-accessed templates where the length of both lists will be small. Ideally, refactor your template's data to avoid this problem altogether.
{% for list1item in list1 %}
{% for list2item in list2 %}
{% if forloop.counter == forloop.parentloop.counter %}
{{ list1item }} {{ list2item }}
{% endif %}
{% endfor %}
{% endfor %}
You can't use variables for attribute names, dictionary keys or list indices.
Also range(0,len(data)-1] is not valid python. It should be range(len(data)).
You probably don't need cycle. Maybe what you want is this:
{% for itemlist in data %}
...
<table>
{% for item in itemlist %}
<tr>
<td>{{ item.a }}</td>
<td>{{ item.b }} ... </td>
</tr>
...
{% endfor %}
</table>
{% if not forloop.last %}
<div class="page_break_div">
{% endif %}
{% endfor %}
I wanted to have alternating colours in my table using a style sheet, by passing a list of toggling True/False values. I found this really frustrating. In the end I created a list of dictionary items with the same keys as the fields in the table, plus one more with the toggling true/false value.
def jobListView(request):
# django does not allow you to append stuff to the job identity, neither
# will it allow forloop.counter to index another list. The only solution
# is to have the toggle embedded in a dictionary along with
# every field from the job
j = job.objects.order_by('-priority')
# have a toggling true/false list for alternating colours in the table
theTog = True
jobList = []
for i in j:
myJob = {}
myJob['id'] = i.id
myJob['duty'] = i.duty
myJob['updated'] = i.updated
myJob['priority'] = i.priority
myJob['description'] = i.description
myJob['toggle'] = theTog
jobList.append(myJob)
theTog = not(theTog)
# next i
return render_to_response('index.html', locals())
# end jobDetaiView
and my template
{% if jobList %}
<table border="1"><tr>
<th>Job ID</th><th>Duty</th><th>Updated</th><th>Priority</th><th>Description</th>
</tr>
{% for myJob in jobList %}
<!-- only show jobs that are not closed and have a positive priority. -->
{% if myJob.priority and not myJob.closeDate %}
<!-- alternate colours with the classes defined in the style sheet -->
{% if myJob.toggle %}
<tr class=d1>
{% else %}
<tr class=d0>
{% endif %}
<td><a href="/jobs/{{ myJob.id }}/">{{ myJob.id }}</td><td>{{ myJob.duty }}</td>
<td>{{ myJob.updated }}</td><td>{{ myJob.priority }}</td>
<td class=middle>{{ myJob.description }}</td>
</tr>
{% endif %}
{% endfor %}
</ul>
{% else %}
<p>No jobs are in the system.</p>
{% endif %}
Use forloop.last - True if this is the last time through the loop:
{% if forloop.last %}
{% endif %}
From Built-in template tags and filters