How to display a specific color depending on the values of a variable in Django? - django

For example, if the application.status is "rejected", then background: gray; color: lightgrey. If "accepted" - green and lightgreen. If "in progress", then blue and light blue.
<div>
{% for application in object_list %}
<div style="background: ???; color: ???;">
<span>{{ application.status }}</span>
</div>
</div>
{% endfor %}
</div>

Firstly, be careful about your for loop, which currently produces invalid HTML (Currently each iteration opens 1 div but closes 2 divs). I've made the appropriate correction in my answer below.
Secondly, it is perfectly valid DTL (Django Template Language) to put {% if %}, {% elif %} and {% else %} blocks inside HTML tags, so that you can generate dynamic attributes.
In your case for styling the color by status, it would look something like this:
<div>
{% for application in object_list %}
<div
{% if application.status == "rejected" %}
style="background: gray; color: lightgrey"
{% elif application.status == "accepted" %}
style="background: green; color: lightgreen"
{% endif %}
>
<span>{{ application.status }}</span>
</div>
{% endfor %}
</div>
Depending on your IDE/Text-Editor settings, you may get some weird syntax highlighting when you do this, but it should produce great output. I use this trick all the time myself.

To set the background and text color of a div element based on the value of the application.status variable in Django templates, you can use conditional statements and CSS classes. Here's an example:
Define CSS classes for each status:
CSS
.rejected {
background-color: gray;
color: lightgrey;
}
.accepted {
background-color: green;
color: lightgreen;
}
.in-progress {
background-color: blue;
color: lightblue;
}
In your Django template, use conditional statements to add the appropriate CSS class to the div element based on the application.status value:
HTML
<div>
{% for application in object_list %}
<div class="{% if application.status == 'rejected' %}rejected{% elif application.status == 'accepted' %}accepted{% elif application.status == 'in progress' %}in-progress{% endif %}">
<span>{{ application.status }}</span>
</div>
{% endfor %}
</div>
This will add the appropriate CSS class to each div element based on the value of application.status, which will set the background and text color of the element as defined in the CSS class.

Related

django templates with-tag custom filter

I have a dict of lists containing images. The key is the id of a journey. Now I want to get an image list based on the id so I wrote a filter:
#register.filter
def dict_value(dict, key):
return dict.get(key)
Now I want to use this filter but how? This is what I did:
{% for journey in journeys %}
...
{% with imagelist={{ images|dictvalue:{{journey.id}} }} %}
{% if imagelist %}
<img class="card-img-top" data-src="" style="height: 225px; width: 100%; display: block;" src="{{imagelist.0.url}}" data-holder-rendered="true">
{% else %}
<img class="card-img-top" data-src="" style="height: 225px; width: 100%; display: block;" src="https://via.placeholder.com/348x225.png" data-holder-rendered="true">
{% endif %}
{% endwith %}
...
{% endfor %}
For testing, I want to display the first image if the list is not empty.
But I have a problem with the with-tag. How do I do it correctly?
In a template filter, you do not need to use extra double curly brackets, you can write this as:
{% with imagelist=images|dictvalue:journey.id %}
That being said, it is normally not a good idea to implement business logic in the template. Normally one does this in the view.

Bulk download of images rendered under mulitple categories in a single html page

I have lot of images stored in a single folder in a S3 bucket. There are three categories(let's say A, B and C) and an image would fall into only one of the three categories. I am rendering all the images category wise on view-all.html page. What I am trying to do is add a separate download button beside the name of each category in the view-all.html. Upon clicking on a particular download button, the images present only under that category should be downloaded as a zip-file. Currently the download buttons are not working for any of the categories.
I have edited only the view-all.html and the views.py files. I searched a lot about this but didn't find any exact solution. I am not sure whether I have to add anything else in any other file. Please help me out. Thanks in advance.
view-all.html
{% extends "cms/base.html" %}
{% block title %}
View-all
{% endblock %}
{% block main %}
<style>
*{box-sizing: border-box;}
.wrapper{
overflow-x:scroll;
white-space: nowrap;
}
.container {
background: #ccc;
position: relative;
height: 50%;
display: inline-block;
}
img {
padding: 0;
display: block;
margin: 0 auto auto 0;
margin-left: auto;
margin-right: auto;
max-width: 100%;
}
.overlay {
position: absolute;
bottom: 0;
background: rgb(0, 0, 0);
background: rgba(0, 0, 0, 0.5);
color: #f1f1f1;
width: 93%;
transition: .5s ease;
opacity:0;
color: white;
font-size: 20px;
padding: 20px;
text-align: center;
}
.container:hover > .overlay {
opacity: 1;
}
#media only screen and (max-width : 767px)
{
height: auto;
max-height: none;
}
</style>
{% for category in categories %}
<br /><h3>{{ category.name }}  <button name='downloadbtn' class="btn btn-primary" onclick='bulkdownload()'><i class="fa fa-download"></i> Download</button> </h3>
<div class="wrapper">
<div id="slider4" class="text-center">
{% for image in images %}
{% ifequal image.category category %}
<div class="container">
<img src="S3-url">
<br />
</div>
{% endifequal %}
{% endfor %}
</div>
</div>
{% endfor %}
{% endblock %}
views.py
#login_required
def bulkdownload(request):
if(request.GET.get('downloadbtn')):
images = Images.objects.all().filter(category = request.category.name)
if images:
categories = request.category.name
zip_subdir = '{:%B %d, %Y}'.format(datetime.now()) # name of the zip file to be downlaoded
zip_filename = '%s.zip' % zip_subdir
# Open StringIO to grab in-memory ZIP contents
s = io.StringIO.StringIO()
# The zip compressor
zf = zipfile.ZipFile(s, 'w')
for fpath in images:
# Calculate path for file in zip
fdir, fname = os.path.split(fpath)
zip_path = os.path.join(zip_subdir, fname)
# Add file, at correct path
zf.write(fpath, zip_path)
# Must close zip for all contents to be written
zf.close()
# Grab ZIP file from in-memory, make response with correct MIME-type
resp = HttpResponse(s.getvalue(), content_type = 'application/x-zip-compressed')
# ..and correct content-disposition
resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
return resp
You can create forms and POST the data to download the images as:
{% for category in categories %}
<form method='POST' action="{% url 'your_url_name_for_view' %}">
<br /><h3>{{ category.name }}  
<input type="hidden" value="{{ category.id }}" name="category_id">
<input name='downloadbtn' class="btn btn-primary" type="submit"><i class="fa fa-download"></i> Download</h3>
</form>
<div class="wrapper">
<div id="slider4" class="text-center">
{% for image in images %}
{% ifequal image.category category %}
<div class="container">
<img src="S3-url">
<br />
</div>
{% endifequal %}
{% endfor %}
</div>
</div>
{% endfor %}
Now in your view you can check for the category as:
if request.method == 'POST':
category_id = request.POST.get('category_id')
images = Images.objects.all().filter(category_id = category_id)
Hope it helps.

count number of instances django template

I've read all the other answers posted with some saying you can and some saying you can't so I tried. What I'm trying to do is iterate my array and if the object's id matches, increment the counter and display it later. I have it set up like this:
{% with elect_count=1 %}
{% for device in devices %}
{% if device.ixDeviceType.ixDeviceClass_id == 3 %}
{{ elect_count|add:1 }}//increment the counter
{% endif %}
{% endfor %}
<li style="list-style: none;" class="padding5">
<span class="glyphicon glyphicon-flash" style="color: yellow; font-size: larger;"></span>
<span name="meter-count">{{ elect_count }}</span>//display total number of elect_count
<ul id="meter-summary">
</ul>
</li>
{% endwith %}
but when I run this, it displays 1 1 1 1 instead of incrementing this elect_count variable and when I do {{ elect_count }} it's 0
You cannot save the counter in template, but you could do it in views and pass it in context:
views.py
def view_function(request):
elect_count = 0
for device in devices:
if device.ixDeviceType.ixDeviceClass_id == 3:
elect_count += 1
context['elect_count'] = elect_count

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.

django templates: loop through list of classes/strings

Is there something like:
{% for class in "red", "green", "blue" %}
<div class="{{ class }}"></div>
{% endfor %}
in django templates?
Well, not directly but you can use cycle combined with a list on the fly:
{% for cnt in "123"|make_list %}
<div class="{% cycle 'red' 'green' 'blue' %}"></div>
{% endfor %}
... another option would be to give your css_classes more generic names like: color_1, color_2, color_3 and then:
{% for cnt in "123"|make_list %}
<div class="color_{{ cnt }}"></div>
{% endfor %}
This would de-couple your css-classes from fixed colors, which is maybe a good idea, if you change the colors later on your css.
---- Update ---
Ok, reading the answers on the link posted by nickromano, I now realize I was wrong. There IS a way of using expr for declaring a real list object on the template. And the split method is better than make_list + cycle :-)