Django: Fill the gaps in a view which is grouped by month - django

My problem seems to be quite standard and I found many solutions, but none which works for a Django view.
I have timestamped items ("conversions") and I want to show the number of items per month. My view deliver this:
2020-05 3
2020-03 2
What I need is this:
2020-05 3
2020-04 0
2020-03 2
models.py:
class Conversion(models.Model):
timestamp = models.DateTimeField()
views.py (thanks to this help):
from django.db.models import Count
from django.db.models.functions import TruncMonth
def conversions_monthly(request):
conv = Conversion.objects.values(month=TruncMonth(
'timestamp')).annotate(count=Count('pk')).order_by('-month')
context = {'conversion': conv}
return render(request, 'dummy/conversions_monthly.html', context)
Template conversions_monthly.html:
{% extends "dummy/base.html" %}
{% block content %}
<table>
{% for c in conversion %}
<tr>
<td>{{ c.month |date:"Y-m" }}</td>
<td class="text-right">{{ c.count }}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}
Data:
[
{
"model": "dummy.conversion",
"pk": 1,
"fields": {
"project": 1,
"timestamp": "2020-03-10T05:00:00Z"
}
},
{
"model": "dummy.conversion",
"pk": 2,
"fields": {
"project": 1,
"timestamp": "2020-03-12T17:00:00Z"
}
},
{
"model": "dummy.conversion",
"pk": 3,
"fields": {
"project": 1,
"timestamp": "2020-05-19T12:00:00Z"
}
},
{
"model": "dummy.conversion",
"pk": 4,
"fields": {
"project": 2,
"timestamp": "2020-05-20T16:10:03Z"
}
},
{
"model": "dummy.conversion",
"pk": 5,
"fields": {
"project": 1,
"timestamp": "2020-05-20T16:30:00Z"
}
}
]
I guess, I have to "aggregate" somehow the min and max date and need something like:
import datetime
from dateutil.relativedelta import relativedelta
result = []
max = date(2020, 5, 20)
min = date(2020, 3, 1)
current = min
while current <= max:
result.append(current)
current += relativedelta(months=1)
which delivers [datetime.date(2020, 3, 1), datetime.date(2020, 4, 1), datetime.date(2020, 5, 1)]
I have no idea how to get these pieces together – or is there a completely different approach for that in Django?

You are on the right path already, here is your code stitched together
# views.py
def index(request):
result = []
# `min` and `max` are keywords in python, so it is bad practice to use them as variable names
_max = datetime.date(2020, 5, 20)
_min = datetime.date(2020, 3, 1)
current = _min
while current <= _max:
result.append({"date": current, "count": 0})
current += relativedelta(months=1)
conversions = Conversion.objects.values(month=TruncMonth(
'timestamp')).annotate(count=Count('pk')).order_by('-month')
# the conditions for checking `i` being smaller than `length` protect you in case there are no conversions in your range
i = 0
length = len(result)
for conversion in conversions:
while i < length and \
not (
result[i]["date"].year == conversion["month"].year and
result[i]["date"].month == conversion["month"].month
):
i += 1
if i < length:
result[i]["count"] = conversion["count"]
context = {'conversion': result}
return render(request, 'dummy/conversions_monthly.html', context)
template
{% extends "dummy/base.html" %}
{% block content %}
<table>
{% for c in conversion %}
<tr>
<td>{{ c.date |date:"Y-m" }}</td>
<td class="text-right">{{ c.count }}</td>
</tr>
{% endfor %}
</table>
{% endblock content %}

Inspired by Nader Alexan I found the following solution which delivers the desired result. _min and _max are no longer fix, but calculated now.
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.db.models import Max, Min
def conversions_monthly(request):
conversions = Conversion.objects.values(month=TruncMonth('timestamp')).annotate(
count=Count('pk'))
result = []
_min = Conversion.objects.aggregate(Min('timestamp'))[
'timestamp__min'].date().replace(day=1)
_max = Conversion.objects.aggregate(Max('timestamp'))[
'timestamp__max'].date().replace(day=1)
current = _max.replace(day=1)
while current >= _min:
c = conversions.filter(month__year=current.year).filter(
month__month=current.month)
if c:
result.append({"date": current, "count": c[0]['count']})
else:
result.append({"date": current, "count": 0})
current -= relativedelta(months=1)
context = {'conversion': result}
return render(request, 'dummy/conversions_monthly.html', context)

Related

how to send just one part of an object to template in django

I want to send just one part of an object to template.
I Have 2 models like below:
class Person(models.Model):
name = models.CharField(max_legth=50)
sure_name = models.CharFiled(max_length=50)
class Office(models.Model):
location = models.CharField(max_legth=50)
and I also created a model like below:
class PersonOffice(models.Model):
person = models.ForeignKey(Person)
office = models.ForeignKey(Office)
now, when I take data from database, by PersonOffice.objects.all(),the data is like below:
{
{
"pk": 0,
"person": {
"pk":0,
"name":"Harry",
"sure_name":"Potter"
},
"office":{
"pk":5,
"location":"Toronto"
}
},
{
"pk": 1,
"person": {
"pk":6,
"name":"John",
"sure_name":"Kelly"
},
"office":{
"pk":6,
"location":"NewYork"
}
}
}
I want to send just offices to the template by render function. How can I do it?
thanks
Updated
I want to use it in template with {% for %} loop. for example:
{% for office in Offices %}
<p>Office location: {{ office.location }}</p>
{% endfor %}
I used below code:
offices = PersonOffice.objects.all().values('office')
print(offices)
and below is the result in terminal:
<QuerySet [{'office': 1}, {'office': 2}, {'office': 11}]>
You can use values
PersonOffice.objects.values('office')
This will output a queryset with office for each object.
EDIT:
PersonOffice.objects.values('office__location')

How to update a specific value inside a dictionary using JQuery and Flask?

I've created a session list that contains my products, i need to update the quantity of any product by increasing it amount, for that am using an HTML type="number" , i also created a function which take the changed amount and multiplying it value with the current quantity, so lets say the amount of the first product by default is 2 by increasing the number lets say 2 the product amount will become 4 and so on, also the price will be multiplied .
Here are the codes:
<th style="text-align: center;" class="amount-izd">{{value["amount"]}}</th>
<th style="text-align: center; width: 14%;">
<div class="block">
<input type="number" id="myNumber" value="1" min=1 data-amount='{{value["amount"]}}' data-pros='{{value["id"]}}' data-price='
{% if g.currency == "euro" %}
{{format_price(value["price"] * config.SITE_CURRENCIES["euro"]).rsplit(".",1)[0]}}
{% elif g.currency == "dollar" %}
{{format_price(value["price"] * config.SITE_CURRENCIES["dollar"]).rsplit(".",1)[0]}}
{% else %}
{{format_price(value["price"] * config.SITE_CURRENCIES["ruble"]).rsplit(".",1)[0]}}
{% endif %}
'>
<label for="myNumber">qty</label>
</div>
</th>
JQuery codes:
$("input[type='number']").bind('keyup change click', function (e) {
if (! $(this).data("previousValue") ||
$(this).data("previousValue") != $(this).val()
)
{
var currentAmount = $(this).attr('data-amount');
var currentPrice = $(this).attr('data-price');
$(this).closest('tr').find('.amount-izd').text(parseInt(currentAmount) * $(this).val());
$(this).closest('tr').find('.price-izd').text(parseInt(currentPrice) * $(this).val());
$.ajax({
type: 'post',
url: '/standard-{{g.currency}}/profile/'+$(this).attr("data-pros")+'/update/price/' + parseInt(currentPrice) * $(this).val(),
cache: false
}).done(function(data){
if(data.error){
toastr.error(data.error)
}
});
$(this).data("previousValue", $(this).val());
} else {
}
});
And finally views.py :
#profile_route.route("/standard-<set_curr>/profile/cart/", methods=['GET','POST'])
#authorize
def cart_products():
if "cart" not in session:
return render_template("my-cart.html", display_cart = {}, total = 0)
else:
items = session["cart"]
dict_of_products = {}
total_price = 0
for item in items:
product = Goods.query.get(item)
total_price += product.price
if product.id in dict_of_products:
pass
else:
dict_of_products[product.id] = {"qty":1, "name":product.product_name, 'category':product.Category.name, "sizes": product.sizes, "hex_color":product.hex_color, "text_color":product.text_color, "material":product.material, "article":product.article, "price":product.price, "sort": product.sort, "amount": product.amount, 'slug':product.slug, 'public_id' : product.public_id, "id":product.id}
return render_template("my-cart.html", display_cart=dict_of_products, total = total_price)
#profile_route.route("/standard-<set_curr>/profile/<int:id>/update/price/<price>", methods=['GET','POST'])
#login_required
def update_price(id, price):
items = session["cart"]
dict_of_products = {}
for item in items:
product = Goods.query.get(item)
if product.id in dict_of_products:
dict_of_products[id]['price'] = price
return jsonify(success=dict_of_products[id]['price'])
return jsonify(error='No product found.')
If i changed the amount , in console i got a 500 error that says:
return jsonify(success=dict_of_products[id]['price'])
KeyError: 47
Please how to overcome this problem ?
Update:
I was wondering , is it possible to update any value of the dictionary by accessing it directly from JQuery ??

Add Calendar Widget to Django Form

I have a view on my website that uses a couple of Django forms to allow the user to specify a date range. I was able to get it so that one Django form creates a start and end field and that when the user clicks on those fields a calendar widget (from here) pops up that allows the user to select a date range. However, when the user selects the date range and hits "apply" the form fields aren't updated.
EDIT
The form I'm using looks like this:
class DateRangeForm(forms.Form):
def __init__(self, *args, **kwargs):
initial_start_date = kwargs.pop('initial_start_date')
initial_end_date = kwargs.pop('initial_end_date')
required_val = kwargs.pop('required')
super(DateRangeForm,self).__init__(*args,**kwargs)
self.fields['start_date'].initial = initial_start_date
self.fields['start_date'].required = required_val
self.fields['end_date'].initial = initial_end_date
self.fields['end_date'].required = required_val
start_date = forms.DateField()
end_date = forms.DateField()
The view they are used in looks like this:
def table_search(request):
initial_start = "2015/2"
initial_end = "2015/222"
message = {'last_url':'table_search'}
if request.method == "POST":
daterange_form = DateRangeForm(request.POST,required=True,initial_start_date=initial_start,initial_end_date=initial_end)
else:
daterange_form = DateRangeForm(required=True,initial_start_date=initial_start,initial_end_date=initial_end)
search_dict.update({'daterange_form':daterange_form})
return render(request, 'InterfaceApp/table_search.html', search_dict)
The Django template here:
<div class="container">
<form action="/InterfaceApp/home/" method="post" class="form">
{% csrf_token %}
<div class="daterangepicker-container mcast-search-filter">
<div class="daterangepicker-label">Date range:</div>
<div id="daterange" class="daterangepicker-content">
{% bootstrap_form daterange_form %}
<i class="icon-calendar icon-large"></i>
</div>
</div>
</form>
</div>
<script>
// the start_date and end_date are the ids that django form fields created
$("#daterange").daterangepicker({
locale: {
format: 'YYYY-MM-DD'
},
startDate: '{{daterange_form.start_date.value}}',
endDate: '{{daterange_form.end_date.value}}'
});
</script>
EDIT 2
And the forms currently look like this (after #ShangWang suggestion) rendered:
Is there a way to display it so the start and end date fields show up? I tried changing the div class so it wasn't hidden, and then they showed up but seemed superfluous.
I use bootstrap-daterangepicker: https://github.com/dangrossman/bootstrap-daterangepicker. It would bind the widget's change to your django form field, so you don't need to manipulate the data once it comes to the views.py.
To get more details you should download and play with it, but here's a rough idea:
Your form.py:
class DateRangeForm(forms.Form):
start_date = forms.DateField()
end_date = forms.DateField()
def __init__(self, *args, **kwargs):
# initialize the start and end with some dates
Your template:
<div class="daterangepicker-container mcast-search-filter">
<div class="daterangepicker-label">Date range:</div>
<div id="daterange" class="daterangepicker-content">
<i class="icon-calendar icon-large"></i>
<span></span> <b class="caret"></b>
</div>
</div>
<!-- This is a hidden div that holds your form fields -->
<div class="hide">From {{ daterange_form.start_date }} to {{ daterange_form.end_date }}</div>
To trigger the widget you need a javascript binding:
// the id_start_date and id_end_date are the ids that django form fields created
$("#daterange").initDateRangePicker("#id_start_date", "#id_end_date");
I created a datepicker wrapper, and defined the initDateRangePicker function. You should put following code in a file called daterangepicker.js and import that in your template as well(or simply copy it into your template):
(function($) {
$.fn.initDateRangePicker = function(start_date_el, end_date_el, future) {
return this.each(function() {
var start = moment($(start_date_el).val());
var end = moment($(end_date_el).val());
var display_date = function(start, end) {
var str = ""
str += start.format('MMMM Do, YYYY');
str += " - ";
str += end.format('MMMM Do, YYYY');
return str;
};
$(this).find("span").html(display_date(start, end));
var self = this;
if(!future) {
$(this).daterangepicker({
format: 'YYYY-MM-DD',
timePicker: false,
ranges: {
'Last 7 days': [moment().subtract('days', 6), moment()],
'Month to date': [
moment().startOf('month'),
moment(),
],
'Last Month': [
moment().subtract('month', 1).startOf('month'),
moment().subtract('month', 1).endOf('month'),
]
},
}, function(start, end) {
$(start_date_el).val(start.format('YYYY-MM-DD'));
$(end_date_el).val(end.format('YYYY-MM-DD'));
$(self).find("span").html(display_date(start, end));
});
}
else {
$(this).daterangepicker({
format: 'YYYY-MM-DD',
timePicker: false,
ranges: {
'Next 7 days': [moment().add('days', 1), moment().add('days', 7)],
'Next month': [
moment().add('month', 1).startOf('month'),
moment().add('month', 1).endOf('month'),
],
},
}, function(start, end) {
$(start_date_el).val(start.format('YYYY-MM-DD'));
$(end_date_el).val(end.format('YYYY-MM-DD'));
$(self).find("span").html(display_date(start, end));
});
}
});
};
}).call(this, jQuery);

Django template - For loop through two values each time

Im working with views and templates.
I've this list in my view
[u'kevinelamo', 50, u'kevin', 4200, u'andres', 200, u'user342', 0, u'cateto', 0]
and I send it to the template..
In the template is parsed like this automatically:
[{"username": "kevinelamo", "time": 50}, {"username": "kevin", "time": 4200}...]
I want to iterate like this:
{% for username,time in llistat_usuaris %}
<h1>My name is <h1>{{username}}
{{time}}
{% endfor %}
But this gave me one char of the list
My name is
[
My name is
{
My name is
"
My name is
u
My name is
s
My name is
e
My name is
r
My name is
n
My name is
a
My name is
m
My name is
e
My name is
How can I handle it? Thanks
If you have this list:
l = [u'kevinelamo', 50, u'kevin', 4200, u'andres', 200, u'user342', 0, u'cateto', 0]
You could convert it to a dictionary:
l_dict = dict(zip(l[::2], l[1::2]))
Which will make l_dict:
{u'andres': 200, u'cateto': 0, u'user342': 0, u'kevin': 4200, u'kevinelamo': 50}
Then iterate over key value pairs in your template:
{% for username, time in l_dict.items %}
<h1>My name is <h1>{{ username }}
{{ time }}
{% endfor %}

django-datatables-view not displaying content returned from AJAX request?

My Ajax request to update the tables is not working. Attached is the screenshot. It simply displays "processing" but nothing happens. The JSON object that is returned (if i visit the URL -{% url 'search_list_json' %} --used in AjaxSource of Js code below) seems to be correct. but still the mainpage is not displaying the JSON content in table.
Here it is:
{"result": "ok",
"iTotalRecords": 1, "aaData": [["<center><font color=\"red\">Mazda=>626:2012-1986</font>\n </center>", "04/07/2014", "10000", "1000", "<center><a href='/search/update/1/'><img src='/static/images/icons/icon_changelink.gif'></a> <a href='/search/delete/1/'><img src='/static/images/icons/icon_deletelink.gif'></a></center>"]],
"sEcho": 0,
"iTotalDisplayRecords": 1}
My views.py
class SearchListJson(BaseDatatableView):
model = Search
columns=['title','created','max_price', 'min_price','actions']
order_columns = ['created', 'max_price']
max_display_length = 500
def render_column(self, row, column):
user = self.request.user
url_edit=static('images/icons/icon_changelink.gif')
url_delete=static('images/icons/icon_deletelink.gif')
#print url_edit, url_delete
if column == 'title':
value = '{0}=>{1}:{2}-{3}'.format(row.vehicle_make,row.vehicle_model,
row.max_year,row.min_year)
edit_url = reverse('search_detail', args=(row.id,))
#print self.get_value_cell_style(edit_url, value,'red')
return self.get_value_cell_style(edit_url, value,'red')
elif column == 'max_price':
#print '%s' %row.max_price
return '%s' %row.max_price
elif column == 'min_price':
#print '%s' %row.min_price
return '%s' %row.min_price
elif column == 'created':
#print row.created.strftime('%m/%d/%Y')
return row.created.strftime('%m/%d/%Y')
elif column == 'actions':
print "in columns actions"
edit_link = """<a href='%s'><img src='%s'></a>""" %(\
reverse('search_update', args=(row.id,)),url_edit)
delete_link = """<a href='%s'><img src='%s'></a>""" %(\
reverse('search_delete', args=(row.id,)),url_delete)
print "edit_link", edit_link
print "delete_link",delete_link
return '<center>%s %s</center>' % (edit_link, delete_link)
else:
return super(SearchListJson, self).render_column(row, column)
def get_value_cell_style(self, url, value, color=None):
style = '''<center>%s</center>''' % (url, value)
if color:
style = '''<center><font color="%s">%s</font>
</center>''' % (url, color, value)
return style
def get_initial_queryset(self):
"""
Filter records to show only entries from the currently logged-in user.
"""
#print "get intial queryset called"
#print Search.objects.filter(user=self.request.user)
return Search.objects.filter(user=self.request.user)
The js-code is below:
$(document).ready(function() {
var oTable = $('#search_table').dataTable( {
"sDom": 'T<"clear">lrtip',
"bProcessing": true,
"bServerSide": true,
"sAjaxSource": "{% url 'search_list_json' %}",
"aaSorting": [ [1,'desc'], [2,'desc'] ],
// Disable sorting for the Actions column.
"aoColumnDefs": [ { "bSortable": false, "aTargets": [ 4 ] } ]
} );
} );
The HTML is:
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="well">
<table id="search_table">
<thead>
<tr>
<th width="10%"><center>Title</center></th>
<th width="15%">Date Created</th>
<th width="15%">Min Price</th>
<th width="15%">Max Price</th>
<th width="10%"></th>
</tr>
</thead>
</table><br>
</div>
</div>
</div>