Django: unable to make a calculation inside a template - django

I've created a e-commerce Django application and in the back office of this application, I have a page that is supposed to show some statisctics.
I'm trying to display benefits or losses.
For the costs, I've created a #property in the model as following:
class Statistics(models.Model):
"""
The Statistics model represents the statistics that can be calculated
"""
costs_infra = models.FloatField(verbose_name="Costs Infrastructure")
costs_salary = models.FloatField(verbose_name="Costs Salary")
class Meta:
verbose_name_plural = "Statistics"
def __str__(self):
"""Unicode representation of Statistics"""
return " Infra costs: {}, Salary costs: {}".format(
self.costs_infra,
self.costs_salary
)
#property
def calculate_costs(self):
return self.costs_infra + self.costs_salary
For the total income, I've calculated it inside a view as following:
#group_required('Administrator', 'Manager')
def stats_home(request):
total_users = User.objects.all().count()
costs = Statistics.objects.all()
subscriptions_1month = Subscription.objects.get(plan_name='1 Month')
subscriptions_1year = Subscription.objects.get(plan_name='1 Year')
subscriptions_3year = Subscription.objects.get(plan_name='3 Years')
user_subscriptions_1month = UserSubscription.objects.filter(subscription=subscriptions_1month).annotate(Count('user', distinct=True)).count()
user_subscriptions_1year = UserSubscription.objects.filter(subscription=subscriptions_1year).annotate(Count('user', distinct=True)).count()
user_subscriptions_3years = UserSubscription.objects.filter(subscription=subscriptions_3year).annotate(Count('user', distinct=True)).count()
income_per_subscription_1month = Subscription.objects.get(plan_name='1 Month').price * UserSubscription.objects.filter(subscription=subscriptions_1month).count()
income_per_subscription_1year = Subscription.objects.get(plan_name='1 Year').price * UserSubscription.objects.filter(subscription=subscriptions_1year).count()
income_per_subscription_3years = Subscription.objects.get(plan_name='3 Years').price * UserSubscription.objects.filter(subscription=subscriptions_3year).count()
total_income = income_per_subscription_1month + income_per_subscription_1year + income_per_subscription_3years
return render (request, "stats_home.html", locals())
An finally, I'm trying to make a simple calculation (total income - total costs) but I'm unable to do this inside the template, as far as I could see and my research led me.
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load has_group %}
{% block title %} Statistics Home {% endblock %}
{% block content %}
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white">Total number of users subscribed to the site:</h5>
<div class="card-body">
{{total_users}}
</div>
</div>
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white">Total number of users per subscription type:</h5>
<div class="card-body">
<br>1 Month: {{user_subscriptions_1month}}
<br>1 Year: {{user_subscriptions_1year}}
<br>3 Years: {{user_subscriptions_3years}}
</div>
</div>
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white">Costs:</h5>
<div class="card-body">
{% for cost in costs %}
<p>Infrastructure Costs: {{cost.costs_infra}}</p>
<p>Salary Costs: {{cost.costs_salary}}</p>
<p>Total Costs: {{cost.calculate_costs}}</p>
{% endfor %}
</div>
</div>
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white"> Total Income: </h5>
<div class="card-body">
{{total_income}}
</div>
</div>
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white"> Benefits/Loss: </h5>
<div class="card-body">
Benefits/Loss: {{total_income - cost.calculate_costs}}
</div>
</div>
By doing {{total_income - cost.calculate_costs}} i get an error
Could not parse the remainder: ' - cost.calculate_costs' from 'total_income - cost.calculate_costs'
The thing is that i can get the total costs with {{cost.calculate_costs}} and the total income with {{total_income}}, but somehow i'm unable to make a simple substraction inside the template.
What would be the best way to achieve this?

Your issue is that you are trying to access cost.calculate_costs outside of loop {% for cost in costs %}. Since cost is not declared, you are getting this error. On top of that, django doesn't natively support calculation in template - reason is that it's best to keep logic entirely in views and leave only presentation in templates. There is however addition filter, but you still need to calculate total_costs.
Your solution is calculate total_profit in view:
def stats_home(request):
...
total_costs = sum(cost.calculate_costs for cost in costs)
total_profit = total_income - total_costs
return render (request, "stats_home.html", locals())
<div class="card border-dark mb-4" id="profil">
<h5 class="card-header bg-dark text-white"> Benefits/Loss: </h5>
<div class="card-body">
Benefits/Loss: {{ total_profit }}
</div>
</div>

The best thing you can do is, rather than doing calculation in templates do it in views as it is recommended not to do this in templates as it will decrease the performance of the website do this instead:
views.py
variable = total_income - total_costs
context = {"variable":variable}
return render(request, "stats_home.html", locals(), context)

Related

Getting an empty query set Django

I'm trying to develop a search functionality but only getting a empty query set every single time .
class SearchView(TemplateView):
template_name = "search.html"
def get_context(self, **kwargs):
context = super().get_context(**kwargs)
kw = self.request.GET.get("search")
results = Thread.objects.filter(Q(heading__icontains=kw) | Q(thread_content__icontains=kw))
print(results)
context["results"] = results
return context
Template
{% extends 'base.html' %}
{% block title %}Search{% endblock %}
{% block content %}
<div class = "container">
<div class = "row">
<div class = "col-md-12">
<h3>Search results for <span class="text-info">"{{ request.GET.search }}"</span></h3>
<h3>{{results}}</h3>
<hr>
{% for item in results %}
<div class="col-md-4">
<img src = "{{item.image.url}}" class="img-fluid" alt = "">
</div>
<div class="col-md-8">
<h4>{{item.heading}}</h4>
<p>{{item.thread_content}}</p>
</div>
{%endfor%}
</div>
</div>
</div>
{% endblock %}
request.GET.search is returning correctly , but the rest is not getting displayed
This was a real strange issue. The Browser cache took a long time to refresh with my updated code. Once I cleared the Browser Cache and Settings, it started working again.

How to pass object properties from view to template in Django?

I must be missing something really basic here. I have created an object in my view to pass to my template. All the properties of my object are passed successfully but the last property, an integer, is not shown when rendering my template. Why?
Here is my code:
Views.py:
def courseListView(request):
object_list = {}
courses = Course.objects.all()
for course in courses:
object_list[course] = {
'title': course.title,
'slug': course.slug,
'thumbnail': course.thumbnail,
'get_absolute_url': '/' + course.slug,
'progress': int(lessonsCompleted.objects.filter(user=request.user.id, course=course.id).count() / course.lessons.count() * 100)
}
print(object_list)
context = {
'object_list': object_list,
}
return render(request, "courses/course_list.html", context)
This way I am creating an object that looks like this when I print it:
{<Course: Mezclar música chill y ambient>: {'title': 'Mezclar música chill y ambient', 'slug': 'mezcla-chill-ambient', 'thumbnail': <ImageFieldFile: mixing.jpeg>, 'get_absolute_url': '/mezcla-chill-ambient', 'progress': 66}, <Course: Componer bases electrónicas>: {'title': 'Componer bases electrónicas', 'slug': 'componer-bases-electronicas', 'thumbnail': <ImageFieldFile: beats.jpeg>, 'get_absolute_url': '/componer-bases-electronicas', 'progress': 75}, <Course: Melodías ultrasónicas>: {'title': 'Melodías ultrasónicas', 'slug': 'melodias-ultrasonicas', 'thumbnail': <ImageFieldFile: melodies.jpeg>, 'get_absolute_url': '/melodias-ultrasonicas', 'progress': 50}}
Ultimately I want to pass to the template the progress for each course, for the currently logged user, so I can show this information for each course in my web page.
To show that, in my template I am using this code:
{% for object in object_list %}
<div class="row justify-content-center">
<div class="col-auto mb-3">
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="/static/{{ object.thumbnail }}" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">{{ object.title }}</h5>
<div class="w3-light-grey w3-round-xlarge">
<div class="w3-container w3-blue w3-round-xlarge" id="progress-{{ object.slug }}" style="width:{{ object.progress }}%">{{ object.progress }}%</div>
</div>
<div class="card-footer">
Ver curso
</div>
</div>
</div>
</div>
</div>
{% endfor %}
All other properties of my object are rendered correctly: title, slug, thumbnail and get_absolute_url. However, progress, eventhough it is printed correctly in my terminal where I am running Django, when my template is rendered, this bit {{ object.progress }} is not rendering the desired result. It should be rendering the integer that represents the percentage completed for each course. Instead, it renders nothing:
https://imgur.com/a/SOIvJ5U
I can not see what I am missing. Any help would be really appreciated, I am stuck, really close to my aim of being able to show the progress for each course for the logged user.
Finally, thanks to another user I have understood the problem. The problem I had is I am not passing an object, actually it is a dictionary, it's not the same thing. If I want to access the properties of my dictionary here is what I need to do:
{% for object, props in object_list.items %} instead of {% for object in object_list %}.
In this way I can access in my for loop the property 'prog' as I wanted.
Here is how it looks like now that it is working:
views.py:
def courseListView(request):
object_list = {}
courses = Course.objects.all()
for course in courses:
object_list[course] = {
'prog': int(lessonsCompleted.objects.filter(user=request.user.id, course=course.id).count() / course.lessons.count() * 100),
}
print(courses)
print(request.user)
print(object_list)
context = {
'object_list': object_list,
}
return render(request, "courses/course_list.html", context)
Note that now I am only passing the 'prog' kay in my dictionary. I don't need the others as they are in the object anyways.
template:
<div class="container d-flex justify-content-center">
<div class="row">
<h1>Cursos</h1>
<div class="container-fluid mt-4 d-flex card-deck justify-content-center">
{% for object, props in object_list.items %}
<div class="row justify-content-center">
<div class="col-auto mb-3">
<div class="card" style="width: 18rem;">
<img class="card-img-top" src="/static/{{ object.thumbnail }}" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">{{ object.title }}</h5>
<div class="w3-light-grey w3-round-xlarge">
<div class="w3-container w3-blue w3-round-xlarge" id="progress-{{ object.slug }}" style="width:{{ props.prog }}%;">{{ props.prog }}%</div>
</div>
<div class="card-footer">
Ver curso
</div>
<script>console.log({{ object_list }});</script>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>

How to make a div appear only once in a loop?

I've created a model Team in models.py in Django for which I've created in views.py the following code:
def team(request):
obj = Team.objects.all().order_by('?')
context = {'team': obj}
return render(request, 'website/team.html', context)
In HTML I've created a team loop which is displaying all the team members available.
{% for team in team %}
<div class="member animated delay1" data-effect="fadeInUp">
<div class="team-image">
<div class="teamDescription">
<p>{{ team.description }}</p>
</div>
<img src="{{ team.picture.url }}">
</div>
<div class="blueLine"></div>
<div class="team-name-function animated delay1" data-effect="fadeInUp">
<h5>{{ team.name }} {{ team.surname }}</h5>
<p>{{ team.title }}</p>
</div>
</div>
{% endfor %}
In this loop, I need to make available one div with the numbers of team members, which has to appear only once and randomly as team members. Currently I have <div class="number">{{ team.count }}</div> outside the loop.
How do I integrate the members counting in the loop and make it appear only once?
Thank you in advance for any solution!
In the view use teams instead of team: context = {'teams': obj}
create a random number between 1 and teams length in view
import random
....
random_number = random.randint(a,len(teams)) # insert it after teams
...
context = {
'team': obj,
'random' : random_number,
}
then in the template use {% for team in teams %}
and if you want to show teams length just one time it's possible to use
{% if forloop.counter==random %}
<div class="number">{{ teams.count }}</div>
{% endif %}

Django - Fetch values from children via Foreign Key, while looping the parent objects in template

I am quite new to Django and probably this will be totaly noobish question, but I am stuck in this situation and bit help would be much appreciated. I have the following models:
class BillType(models.Model):
name = models.CharField(max_length=200)
created_on = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.name
class Bill(models.Model):
bill_type = models.ForeignKey(BillType, on_delete=models.CASCADE)
month = models.CharField(max_length=200)
amount = models.IntegerField()
due_date = models.CharField(max_length=200)
note = models.TextField(blank=True)
recurring = models.BooleanField(default=True)
currency = models.CharField(max_length=100)
created_on = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.month
and the following view:
def billingOverview(request):
bill_types = BillType.objects.all()
context = {
'bill_types': bill_types,
}
return render(request, 'management/billing-overview.html', context)
In the template, I am using the For loop to loop through the BillType objects and that part is working fine. However, for each of the looped BillType objects that are rendered separately in the template, I need to access and show the data for 'month' corresponding to that particular BillType object. Can you help me with an example of how I can do that action?
Thanks,
Bojan
In case the template is needed:
{% for bill_type in bill_types %}
<!-- Billing overview column -->
<div>
<!-- Billing overview widget -->
<div class="bo-widget">
<div class="bo-widget-header">
<div>
<h2>{{ bill_type.name }}</h2>
<span>Overview of {{ bill_type.name|lower }} bills</span>
</div>
<a class="bo-addnew-bill" href="{% url 'newBill' bill_type.id %}">+</a>
</div>
<div class="bo-widget-content">
<span>OVERDUE</span>
<div class="bo-widget-bill-cont overdue">
<span class="bo-overdue-warning"><i class="fas fa-exclamation-triangle"></i></span>
<div class="bo-bill-info">
<span>January 2020</span>
<div>
<span>2,200 MKD</span>
<p>Due Date: 15 February 2020</p>
</div>
</div>
<div class="bo-bill-actions">
<a class="bo-bill-paid-action" href="javascript:;"><i class="fas fa-check"></i></a>
</div>
</div>
<span>LATEST BILL</span>
<!-- Latest Bill -->
<div class="bo-widget-bill-cont">
<div class="bo-bill-info">
<span>{{ MONTH SHOULD GO HERE }}</span>
<div>
<span>2,200 MKD</span>
<p>Due Date: 15 February 2020</p>
</div>
</div>
<div class="bo-bill-actions">
<a class="bo-bill-paid-action" href="javascript:;"><i class="fas fa-check"></i></a>
</div>
</div>
</div>
<div class="bo-see-all">
See all bills
</div>
</div>
</div>
{% endfor %}
Yes. I knew that it would take only two rows of code to fix this, but nevermind. I found the solution and here it is in case somebody needs this:
In the Bill model, I've added related_name 'billtype' to the ForeignKey field, so the entire line now will be:
bill_type = models.ForeignKey(BillType, on_delete=models.CASCADE, related_name='billtype')
Now for the queries in the view, I've added the following line
bill_types = BillType.objects.all()
in order to fetch all of the bill type objects in BillType model; Next, I've added the following line
bills = Bill.objects.select_related('bill_type').all()
in order to fetch all of the objects in the Bill model, of the related field 'bill_type'.
In the template,
{% for bt in bill_types %}
<!-- Billing overview column -->
<div>
<!-- Billing overview widget -->
<div class="bo-widget">
<div class="bo-widget-header">
<div>
<h2>{{ bt.name }}</h2>
<span>Overview of {{ bt.name|lower }} bills</span>
</div>
<a class="bo-addnew-bill" href="{% url 'newBill' bt.id %}">+</a>
</div>
<div class="bo-widget-content">
<span>OVERDUE</span>
<div class="bo-widget-bill-cont overdue">
<span class="bo-overdue-warning"><i class="fas fa-exclamation-triangle"></i></span>
<div class="bo-bill-info">
<span>January 2020</span>
<div>
<span>2,200 MKD</span>
<p>Due Date: 15 February 2020</p>
</div>
</div>
<div class="bo-bill-actions">
<a class="bo-bill-paid-action" href="javascript:;"><i class="fas fa-check"></i></a>
</div>
</div>
<span>LATEST BILL</span>
<!-- Latest Bill -->
<div class="bo-widget-bill-cont">
<div class="bo-bill-info">
<span>{{ bt.billtype.last }}</span>
<div>
<span>{{ bills.amount }} {{ bills.currency}}</span>
<p>{{ bills.due_date }}</p>
</div>
</div>
<div class="bo-bill-actions">
<a class="bo-bill-paid-action" href="javascript:;"><i class="fas fa-check"></i></a>
</div>
</div>
</div>
<div class="bo-see-all">
See all bills
</div>
</div>
</div>
{% endfor %}
I am using For loop to loop through all of the BillType objects and getting the wanted data by using {{ bt.billtype.last }}, in order to get corresponding last object from the model's related field.

combine number of database requests using ORM

I am working on a website, where a user can vote and comment on different projects in various categories. Whenever a user returns to the site, their votes will be highlighted and all the comments will be visible.
The problem is that every time the page is loaded there are many database calls due to the design of my models or due to me not finding an efficient way of retrieving the data.
My models are as follows:
class ProjectCategory:
title=models.CharField(...)
class Project:
category=models.ForeignKey(ProjectCategory)
title=models.CharField(...)
def ownedComments(self):
return Comment.objects.filter(project=self).order_by('-submissionTime')
def ownedCommentsPreview(self):
return Comment.objects.filter(project=self).order_by('-submissionTime')[0:3]
def ownedVotes(self):
return Vote.objects.filter(project=self,upVoted=True).count()
class Vote:
project=models.ForeignKey(Project)
user=models.ForeignKey(User)
vote=models.IntegerField(...)
class Comment:
project=models.ForeignKey(Project)
user=models.ForeignKey(User)
title=models.CharField(...)
Here are relevant parts of my views.py:
def getCurrentUserData(request=None):
if (request==None) or (request.user.is_authenticated()==False):
return {'loggedInUsername':'Anonym','userIsLoggedIn':False}
return {'loggedInUsername':request.user.username,'userIsLoggedIn':True}
def getProjectResponse(request,surveyFile=None,additionalInfo={}):
userVotedUp=[]
if surveyFile!=None:
userVotes=Vote.objects.filter(upVoted=True,surveyFile=surveyFile)
for vote in userVotes:
userVotedUp.append(vote.project.id)
answerDict={'userVotedUp':userVotedUp,'prjCategories':PrjCategory.objects.select_related(),'projects': Project.objects.prefetch_related('category').filter(visible=True),'spammerquestion':spammerquestion.SpammerQuestion().getRandomQuestion(),'rotatingLines':helpers.getRandomRotatingLines(),'backgroundimageurl':helpers.getBackgroundImageUrl()}
answerDict.update(getCurrentUserData(request))
answerDict.update(additionalInfo)
return answerDict
def showProject(request,prjNo):
currentPrj=get_object_or_404(Project,prjNo=prjNo,visible=True)
surveyFile=assignSurveyFile(request,create_surveyFile_if_not_existing=False)
return render_to_response('website/singleproject.html', getProjectResponse(request,surveyFile,{'currentPrj':currentPrj,'immediateProjectID':currentPrj.id}),context_instance=RequestContext(request))
The template contains many iterations over projects and categories, there might be several database calls happening here. Relevant parts of the template are:
{% for project in projects %}
self.vote['{{ project.id }}']=ko.observable(new Vote({'totalVoteNumber':ko.observable({{ project.ownedVotes }}),'userVoteState':ko.observable({% if project.id in userVotedUp %}true{% else %}false{% endif %})}));
self.comments['{{ project.id }}']=ko.observableArray([]);
{% for comment in project.ownedCommentsPreview %}
self.comments['{{ project.id }}'].push(new Comment({'description':'{{ comment.description|striptags|safe }}','submissionTime':'{{ comment.submissionTime|humanDT }}','commentUuid':'{{ comment.commentUuid }}','deletable':false}));
{% endfor %}
{% endfor %}
{% for prjCat in prjCategories %}
.prjCatUnderline{{ prjCat.id }} {border-bottom: 0px solid {{ prjCat.color }};}
.prjCatUnderline{{ prjCat.id }}:hover {border-bottom: 3px solid {{ prjCat.color }};}
{% endfor %}
{% for prjCat in prjCategories %}
{{ prjCat.title }}
{% if not forloop.last %}<span class="catpipe">|</span>{% endif %}
{% endfor %}
<script type="text/javascript">
var projectLocations = [{% for project in projects %}{% if project.location != '' %}['{{ project.title }}', {{ project.location }}, {{ project.id }}, '{{ MEDIA_URL }}{{ project.mapImage }}', {{ project.mapImage.width }}, {{ project.mapImage.height }}, '{{ project.id }}'],
{% endif %}{% endfor %}];
</script>
{% for project in projects %}
<div class="item prjCat{{ project.category.id }}">
<div class="prjnumberbox">
<div class="numberbar" style="background-color:{{ project.category.color }}"></div>
<div class="prjnumber">{{ project.prjNo|stringformat:"02d" }}</div>
<div class="numberbar" style="background-color: {{ project.category.color }}"></div>
</div>
<div class="prjheadline"><span>{{ project.title }}</span></div>
<div class="prjimagecontainer">
<!-- ko with: $root.vote['{{ project.id }}'] -->
<div class="prjimageoverlay"><a data-bind="click: function(data,event) { $root.showSingleProjectData('{{ project.id }}',data) },css : { imgVoted :userVoteState }" class="singleProjectOverlay" href="{% url 'website.views.showProject' prjNo=project.prjNo %}"></a></div>
<div class="prjimage"><img src="{{ MEDIA_URL }}{{ project.teaserImage }}" /></div>
<!-- /ko -->
</div>
<div class="prjtextcontainer" {% if project.ownedComments|length_is:"0" %}style="background-image:none;"{% endif %}>
<div class="prjtext">{{ project.teaserText|safe }} <a class="singleProjectOverlay" data-bind="click: function(data,event) { showSingleProjectData('{{ project.id }}',data) }" href="{% url 'website.views.showProject' prjNo=project.prjNo %}">mehr >></a></div>
<div class="votesline"><span class="voteslinefont" data-bind="with: vote['{{ project.id }}']"> <span class="votecount" data-bind="text: totalVoteNumber">{{ project.ownedVotes }}</span> votes </span></div>
<div class="prjcomments nojs">
{% for comment in project.ownedCommentsPreview|slice:":4" %}
<div class="prjcommentitem">
{{ comment.teaserdescription }}
</div>
{% endfor %}
</div>
<div class="prjcomments" data-bind="foreach: comments['{{ project.id }}'], visible: comments['{{ project.id }}']().length > 0">
<!-- ko if: $index() < 4 -->
<div class="prjcommentitem">
<em data-bind="text: submissionTime"> </em><br/>
<span data-bind="text: description"> </span>
[X]
</div>
<!-- /ko -->
</div>
</div>
</div>
{% endfor %}
For the front page I'd like to retrieve all the projects, most recent three comments, the total number of votes for a project and the comments and votes by the user.
It would be great if that was possible with one db call, or with two, a general one to get the number of votes and the comments, and one for the data specific to the user.
CUrrently I am retrieving all the projects with one call, so I am automatically getting all the project categories. Then I get all the comments at once and assign them to the projects in python. I then get the comments and votes for a certain user with one call each.
Is there a way - without using sql directly - to get
comments and votes for a user with a single database call
comments, projects, accumulated number of votes per project and project categories at once?
One idea would be to download the whole database using select_related() and then sorting everything out in Python, but if in the future I have a large number of votes or comments, this might not be a good idea.