django templates with-tag custom filter - django

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.

Related

How to display a specific color depending on the values of a variable in 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.

How to display content in grid format in django template when receiving dynamic data in terms of list from database?

In django model I have created a multiselectfield for suppose wishlist where user can select multiple wishes from the available choices.
I am storing this data in comma separated format when it comes to displaying this on template things are pretty easy.
But, I want this data to be displayed dynamically as one row with two columns and as soon as two columns are filled and there is more data left to be displayed logic should should have the capability to create a new row and display the remaining content on the django-template.
For reference:
# models.py
class Wishlist(models.Model):
wishlist = (
('W1',"Buy a big masion"),
('W2',"Buy worlds fastest car"),
('W3',"Visit Europe"),
('W4',"Travel on bike to mountains")
)
your_wishlist = MultiSelectField(choices=wishlist)
# views.py
def index(request):
wishlist = Wishlist.objects.all()
context = {
"wishlist":wishlist
}
return render(request,'demolistingapp/index.html',context)
# index.html
{% load app_filters %}
{% block content %}
<h1>INDEX LISTING APP</h1>
{% if wishlist %}
{% for each_wish in wishlist %}
{% with each_wish.your_wishlist|split:"," as wish %}
{% for mywish in wish %}
<p>{{mywish}}</p><br>
{% endfor %}
{% endwith %}
{% endfor %}
{% endif %}
{% endblock %}
I have registered the custom filter split which returns a list.
I want the data to be displayed in terms of grid by maintaining two columns.
Sample output:
enter image description here
You could use cycle in the templates to create different class:
<p class="{% cycle row_left row_right %}">{{ mywish }}</p>
with adequate css (float/clear or inline-block;width:50%).
I would wrap your outer for loop in a HTML element with a class, then apply a grid layout to that class in your stylesheet. Wrapping each wish in a div element with a particular class will help you with further styling too.
<div class="wishlist">
{% for each_wish in wishlist %}
{% with each_wish.your_wishlist|split:"," as wish %}
{% for mywish in wish %}
<div class="wish">
<p>{{mywish}}</p>
</div>
{% endfor %}
{% endwith %}
{% endfor %}
</div>
And in your CSS:
.wishlist {
display: -ms-grid;
display: grid;
-ms-grid-columns: 1fr 1fr;
grid-template-columns: 1fr 1fr;
}

Django iteration (for ... in ...) how to put it in different divs?

I have few instances of model.
my model:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
view:
def allrecords(request):
records = Record.objects.all().order_by('created')
return render(request, 'mycar/allrecords.html', {'records' : records})
I want show it on my website. In my template i have:
{% for record in records %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endfor %}
Now i get list of my records, but i would like put the newest record to first div, next to second etc. How can i do that?
I show simple screen how i would like have that (if someone will create new record, it will go to first div and other records will change place. Is any possibility to do something like that?
edit:
<div>
{% for record in records %}
{% if forloop.counter == 1 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
<div>
{% for record in records %}
{% if forloop.counter == 2 %}
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
{% endfor %}
</div>
.
.
# till your 5th image
You can use forloop.counter to get the iteration number and check what is the iteration the loop and handle data accordingly.
In addition you can use CSS to make the layout work as you want.
Here is the information for Django template counter
Edit :
{% for record in records %}
<div>
{% if forloop.counter == 1 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
<div>
{% if forloop.counter == 2 %}
# Here you can get your first images
<img src={{ record.image.url}}/>
<div>
{{record.name}}
</div>
{% endif %}
</div>
.
.
# till your 5th image
{% endfor %}
There are two ways to do this. If you want to set this option for a single view then:
def all_records(request):
records = Record.objects.all().order_by('-created')
return render(request, 'mycar/allrecords.html', {'records' : records})
You're almost correct but order_by('created') leads to asceding order while order_by('-created') leads to descending order which is what you require.
Alternatively, if you want to have this setting to apply to all views then set class Meta in your models.py which will ensure that wherever you use Record.objects.all() it returns Records in descending order of created field:
class Record(models.Model):
name = models.ForeignKey(Car)
image = models.ImageField(upload_to='images/')
created = models.DateTimeField(
default=timezone.now)
class Meta:
ordering = ('-created')
It's Django design pattern to make all logical decisions in models and views and only just plugin formatted data in templates. You shouldn't add any complex logic in templates.
I'm assuming the question means that the model might have more than 5 records. If so, a more generic solution would be
<div class='row'>
<div class='firstimage'>
<img src={{ records[0].image.url}}/>
{{record.name}}
</div>
{% for record in records %}
{% if forloop.counter > 1 %}
<div class='subsequentimage'>
<img src={{ record.image.url}}/>
{{record.name}}
</div>
{% endif %}
{% cycle "" "</div><div class='row'>" "" %}
{% endfor %}
</div>
Note the use of the 'cycle' tag to begin a new row div every third cell div.
I don't know what your CSS classes are to distinguish between rows and cells so I used 'row', 'firstimage' (which might be defined to take up twice as much width) and 'subsequentimage' as example classes.
I recommend you to use the context variables:
def all_records(request):
records = Record.objects.all().order_by('-created')
newest = records[:5]
oldest = records[5:]
return render(request, 'mycar/allrecords.html', {'newst' : newest,
'oldest': oldest })
In your template :
{% for new in newst %}
<div>what you want with news</div>
{% endfor %}
{% for old in oldest %}
<div>what you want with olds</div>
{% endfor %}

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 :-)

How to add top save button on django admin change list?

I want to have a save button on top of django admin change list page. It seems that django don't have this functionality built-in. The save_on_top option only controls behavior on change form page. Any suggestion is welcome.
In Django 3 (and maybe earlier, not sure) in your custom admin form add save_on_top = True
class MyAdmin(admin.ModelAdmin):
save_on_top = True
First, you need a way to extend the template found at django/contrib/admin/templates/admin/change_list.html. If you don't already know how to do that, check out this answer and this answer.
Next, you need to create your own change_list.html template and put code similar to the following in it. For the sake of simplicity, I've included inline CSS. However, this is bad practice, so you should not do it. Assuming you move the CSS to an external file, you won't need to load admin_static. Lastly, the extends line that you use might not be exactly the same as what I've shown here.
{% extends "contrib/admin/templates/admin/change_list.html" %}
{% load i18n admin_static %}
{% block result_list %}
{% if cl.formset and cl.result_count %}
<div style="border-bottom: 1px solid #ccc; background: white url({% static "admin/img/nav-bg.gif" %}) 0 180% repeat-x; overflow: hidden;">
<p>
<input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/>
</p>
</div>
{% endif %}
{{ block.super }}
{% endblock %}
The {% if %} tag and the <input> tag inside of it is from django/contrib/admin/templates/admin/pagination.html.
The CSS is based on the CSS for #changelist .paginator and is found in django/contrib/admin/static/admin/css/changelists.css.
If you don't mind having pagination links at the top of the page as well, you can do it with a few lines of template code, worksforme in Django 2.0.
Create my_app/templates/admin/my_app/my_model/change_list.html:
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% block result_list %}
{% pagination cl %}
{{ block.super }}
{% endblock %}
This will render the pagination and the save button:
Could benefit from a line or two of CSS, though...