How to loop over Chart.js with Django list view - django

I am using a Django List View, and I am trying to iterate through multiple objects and display percentage values in a Chart.JS gauge.
However, although I am iterating the names of the gauge id's by using a for loop counter, I am only ever getting the first iteration of the chart.js object rendering on my screen. My initial thoughts are that similar to how I am dynamically creating new canvas ids for the chart.js objects, I should be doing a similar thing for the variable I am trying to pass into the chart object e.g. reduction overall, but I am not having any luck. Your feedback is welcome.
Views.py
class PerformanceDetailView(ListView):
template_name = 'performance_detail.html'
model = Performance
context_object_name = 'performance'
def get_queryset(self, **kwargs):
code = self.kwargs.get('code')
return Performance.objects.filter(code=code)
Performance_detail.html
<section class="projects pt-4 col-lg-12">
{% for item in performance %}
<div class="container-fluid pt-2 col-lg-12">
<!-- Project--><div class="project" >
<div class="row bg-white has-shadow" style="height: 14rem">
<div class="left-col col-lg-2 d-flex align-items-center justify-content-between">
<div class="project-title d-flex align-items-center">
<div class="has-shadow"><img src="{% static 'img/avatar-2.jpg' %}" alt="..." class="img-fluid"></div>
</div>
</div>
<div class="right-col col-lg-2 align-items-center vertical-center">
<div class="text">
<h3 class="h2 pt-2">{{item.brand}} {{item.style}}</h3>
<p class="text-muted">{{item.package_type| capfirst}} package, round {{item.testing_round}}</p>
<p class="text-muted">Item size: {{item.size}}</p>
</div>
</div>
<div class="right-col col-lg-8 align-items-center vertical-center">
<div class="row mt-5">
<div class="col-md-3">
<div class="gauge-container">
<canvas id="gauge1-{{ forloop.counter }}" class="gauge-canvas"></canvas><span id="gauge1Value-{{ forloop.counter }}" class="gauge-label"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{{ item.reduction_overall|json_script:"reduction_overall" }}
{{ for_loop_counter|json_script:"for_loop_counter" }}
{% endfor %}
</section>
{% endblock content %}
Block JS within package_detail.html
<!-- Gauge.js-->
<script src="{% static 'vendor/gaugeJS/gauge.min.js' %}"></script>
<script>
$(function () {
var reduction_overall = JSON.parse(document.getElementById('reduction_overall').textContent);
var for_loop_counter = JSON.parse(document.getElementById('for_loop_counter').textContent);
// Gauges
var gauge1 = document.getElementById('gauge1-'+for_loop_counter);
opts.colorStop = "#864DD9";
var gaugeObject1 = new Donut(gauge1).setOptions(opts);
gaugeObject1.maxValue = 100; // set max gauge value
gaugeObject1.setMinValue(0); // set min value
gaugeObject1.set(reduction_overall); // set actual value
gaugeObject1.setTextField(document.getElementById("gauge1Value-" + for_loop_counter));
});
</script>
{% endblock js %}

This was solved by including the JavaScript in the main block content and dynamically naming the gauge elements themselves.
var func_name = "container-{{forloop.counter}}";
func_name = new Donut(gauge1).setOptions(opts);
I posted a full example to a similar problem here Chart JS and Django loop

Related

Showing list of items including item details on same page in Django

In a Django v3.x app I would like to display a list of uploaded file names (e.g. images) in the left hand side of the screen. When a user clicks on one of those, I'd like to display the actual file/image on the right hand side of the screen. I am still new to Django and have used both ListView and DetailView separately, but not in such a combination. I'm not sure how this can be achieved.
Using a little Bootstrap magic, I can create a split screen easily. Hence, my template would look somehow like this:
<div class="row">
<div class="col-md-5 left">
{% for image in images %}
<div class="card">
<h4>{{ image.url }}</h4>
View
</div>
{% endfor %}
</div>
<div class="col-md-5 right">
{# TODO: When the user clicks on the View url above, then I'd
like to load the actual image here on the right hand side of the
screen inside this div-tag. #}
</div>
</div>
Question 1: How can I achieve loading a selected image from a list? Can I still use ListView and DetailView, or do I need to write my own View logic?
Question 2: Ideally, I'd like to NOT re-send the whole page from the server to the client, because the list of images in the lefthand-side could potentially be long and require pagination. So, when the user clicks View, then, ideally, I'd like to load only the document from the server. Is this somehow feasible?
Well I have made a sample code and you can refer to it and get some idea.
<!--Carousel Wrapper-->
<div id="carousel-thumb" class="carousel slide carousel-fade carousel-thumbnails"
data-ride="carousel">
<!--Slides-->
<div class="row">
<div class="col-lg-8">
<div class="carousel-inner" role="listbox">
{% for latest in latest_course %}
<div class="carousel-item {% if forloop.counter0 == 0 %}active{% endif %}">
<img class="d-block w-100" src="{{latest.poster.url}}" alt="First slide">
</div>
{% endfor %}
</div>
</div>
<!--/.Slides-->
<div class="col-lg-4">
<ol class="slider_list">
{% for latest in latest_course %}
<li data-target="#carousel-thumb" data-slide-to="{{forloop.counter0}}"
class="active"> <img class="img-thumbnail" width="100px" height=100px src="
{{latest.poster.url}}"
class="img-fluid"></li>
{% endfor %}
</ol>
</div>
and the output looks like this
Here, you can see the list on the right side and when you select an item the selected item loads in the left side. This way you can style your template the way you want.
basically you want to show the selected item from the list on the other side like a slideshow

unable to retrive data in crud operation

{%for x in read%}
{%if request.user == user%}
<div class="card " >
<h5>
<div class="card-header bg-info">
Dr.{{i.name}}
</div>
</h5>
<div class="card-body">
<h6 class="card-title ">Appointment Date : {{x.appoinment_date}}</h6>
<h6 class="card-title">Age : {{x.age}}</h6>
<h6 class="card-title">Digree : {{x.disease}}</h6>
<h6 class="card-title">Email : {{x.email}}</h6>
</div>
</div>
{%endif%}
{%endfor%}
i am using code above but i am getting data of all users instead of current user
I'm not familiar with your application, but it seems like the if-statement relates to some variable 'user', while in the table everything relates to 'x.something'. If what you're trying to do is filter for rows that are only related to the current user, you may want to check
{%if request.user == x.user%}

Populating dropdown in Django using Ajax

I have several dropdowns in my form, which I am looking to populate using Ajax from backend. Given below are the relevant code segments:
HTML:
<div class="row">
<div class="col-xs-6">
<div class="col-xs-6">
<label name="start_date" class="control-label" style="width:35%">Start date</label>
<input type="date" style="color:black;width:100px" ></input>
</div>
</div>
<div class="col-xs-6">
<label name="end_date" class="control-label" style="width:35%">End Date(Default: Current Date)</label>
<input style="color:black;width:100px" type="date"></input>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<label name="fruit" class="control-label" style="width:35%; padding-left:15px">Select a Fruit</label>
<select style="width:150px;height:30px">
{% for option in options.fruit %}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
</div>
<div class="col-xs-6">
<label name="vendor" class="control-label" style="width:35%">Select a vendor</label>
<select style="width:150px;height:30px">
{% for option in options.vendor %}
{{ option }}
<option value="{{ option }}">{{ option }}</option>
{% endfor %}
</select>
</div>
</div>
{% block script %}
<script>
document.onload = function(){
$.ajax({
url : window.location.href,
type:'GET',
cache:false,
data:{type:'formdata'},
success:function(res){
if(typeof res.options == 'undefined'){
self.options = res.options;
}
if(typeof res.options.start_date == 'undefined'){
self.form.start_date = res.options.start_date;
}
if(typeof res.options.end_date == 'undefined'){
self.form.end_date = res.options.end_date;
}
if(typeof res.options.fruit == 'undefined'){
window.fruit = res.options.fruit;
}
if(typeof res.options.vendor == 'undefined'){
window.vendor = res.options.vendor;
}
},
error : function(err){
self.message = "Error getting data for the form";
}
});
}
</script>
{% endblock %}
Both the drop downs are independent of each other. The data is being given at the front end through this view:
class Search(View):
def get(self, request):
if request.GET.get('type') == 'formdata':
options = {'fruit': [], 'vendor': []}
try:
cursor = connections['RDB'].cursor()
options['end_date'] = today.strftime('%Y-%m-%d')
last_week_date = today - timedelta(days=7)
options['start_date'] = last_week_date.strftime('%Y-%m-%d')
options['fruit'] = [a[0] for a in cursor.fetchall()]
options['vendor'] = [a[0] for a in cursor.fetchall()]
return JsonResponse({'options': options})
The back end is working perfectly fine, the options dictionary is getting populated as I expected it to. However the drop down options are not showing up on the front end. Where am I going wrong? Any help is greatly appreciated.
Maybe try using select2 it's a pretty good utility for populating select boxes through Ajax, even has a search functionality in built. Here's a simple example.
html -
<html>
<head>
<title>Using Select2</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<!-- Select2 CSS -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
</head>
<body>
<div class="jumbotron">
<div class="container bg-danger">
<div class="col-md-6">
<label>Single Select2</label>
<select class="js-data-example-ajax"></select>
</div>
</div>
</div>
<!-- jQuery -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<!-- Select2 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script>
$('.js-data-example-ajax').select2({
ajax: {
url: 'https://api.github.com/search/repositories',
dataType: 'json'
// Additional AJAX parameters go here; see the end of this chapter for the full code of this example
}
});
</script>
</body>
</html>
Don't forget to import the relative JS and CSS CDN's.

set a variable in django template file, and update it after iteration

Trying to set variable in django template, and make a simple rule to update it after iteration. Here is my template:
{% for adv in advs %}
<div class="media-item big" style="top: 18%;left:{% cycle '304' '1078' %}px;">
<div class="media-item__tags">
{{ adv.year }}
{{ adv.payer}}
</div>
<div class="media-item__content">
<div class="media-item__background">
<div class="media-item__canvas">
<div class="media-item__canvas-background" style="background-image: url({{adv.image.url}})"></div>
</div>
<h2 class="topic white upcase fixed-size">{{ adv.name }}</h2>
Смотреть проект
</div>
</div>
</div>
In first div i need to make different 'left:' value. I want to make rule: after every iteration, value changes from base=304 to base+774 px. I tryed to do it somehow with {% cycle %} but it doesnt work for me, also tryed to set variables with {% with %} tag, but didnt find any information about how to update them.
You can set the style by multiplying the current counter from 0...n with 774 and add base value 304. For this, you'll need a custom template tag.
Create a templatetags directory in your app. Add an empty __init__.py and multiply_add.py.
multiply_add.py
from django import template
register = template.Library()
#register.simple_tag
def mul_add(a, b, base_value):
return (a * b) + base_value
template.html
{% load multiply_add %}
{% for adv in advs %}
<div class="media-item big" style="top: 18%;left:{% multiply_add forloop.counter0 774 304 %}px;">
<div class="media-item__tags">
{{ adv.year }}
{{ adv.payer}}
</div>
<div class="media-item__content">
<div class="media-item__background">
<div class="media-item__canvas">
<div class="media-item__canvas-background" style="background-image: url({{adv.image.url}})"></div>
</div>
<h2 class="topic white upcase fixed-size">{{ adv.name }}</h2>
Смотреть проект
</div>
</div>
</div>

Extract information from all matching nodes without looping xpath

<ul class="products-grid">
<li class="item">
<div class="product-block">
<div class="product-block-inner">
<img src="#/producta.jpg">
<h2 class="product-name">Product A</h2>
<div class="price-box">
<span class="regular-price" id="#">
<span class="price">Rs 1,849</span>
</span>
</div>
</div>
</div>
</li>
<li class="item">
<div class="product-block">
<div class="product-block-inner">
<img src="#/productb.jpg">
<h2 class="product-name">Product B</h2>
<div class="price-box">
<span class="regular-price" id="#">
<span class="price">Rs 1,849</span>
</span>
</div>
</div>
</div>
</li>
</ul>
I am at this moment scraping the item in a loop.
products = response.xpath('//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]').extract()
After getting the product-block-inner node, I save it into products and then I will have to loop like
for product in products:
// parse the div.product-block-inner further deep down
// to get name, price, image etc
// and save it to a dict and yeild
pass
Is this possible that i get text, href for all div.product-block-inner in the final list without looping
Yes, but it's very confusing, for example you could try this:
products = response.xpath(
'//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]'
).css(
'.product-name a::attr(href), .product-name a::text, .price::text'
).extract()
but I would suggest to always loop (btw, why do you call extract() when you assign it to products?)
products = response.xpath(
'//ul[#class="products-grid"]//li//div[#class="product-block"]//div[#class="product-block-inner"]'
)
for product in products:
yield {'name': product.css('.product-name a::text').extract_first()
'url': product.css('.product-name a::attr(href)').extract_first()
'price': product.css('.price::text').extract_first()}
(I've used css selectors in this case because the equivalent xpaths are longer, but the same can also be achieved using xpath)