So to display a small bargraph using Django and Chart.js I constructed the following query on my model.
views.py
class BookingsView(TemplateView):
template_name = 'orders/bookings.html'
def get_context_data(self, **kwargs):
today = datetime.date.today()
seven_days = today + datetime.timedelta(days=7)
bookings = dict(Booking.objects.filter(start_date__range = [today, seven_days]) \
.order_by('start_date') \
.values_list('start_date') \
.annotate(Count('id')))
# Edit set default for missing dictonairy values
for dt in range(7):
bookings.setdefault(today+datetime.timedelta(dt), 0)
# Edit reorder the dictionary before using it in a template
context['bookings'] = OrderedDict(sorted(bookings.items()))
This led me to the following result;
# Edit; after setting the default on the dictionary and the reorder
{
datetime.date(2019, 8, 6): 12,
datetime.date(2019, 8, 7): 12,
datetime.date(2019, 8, 8): 0,
datetime.date(2019, 8, 9): 4,
datetime.date(2019, 8, 10): 7,
datetime.date(2019, 8, 11): 0,
datetime.date(2019, 8, 12): 7
}
To use the data in a chart I would like to add the missing start_dates into the dictionary but I'm not entirely sure how to do this.
So I want to update the dictionary with a value "0" for the 8th and 11th of August.
I tried to add the for statement but I got the error;
"'datetime.date' object is not iterable"
Like the error says, you can not iterate over a date object, so for start_date in seven_days will not work.
You can however use a for loop here like:
for dt in range(7):
bookings.setdefault(today+datetime.timedelta(dt), 0)
A dictionary has a .setdefault(..) function that allows you to set a value, given the key does not yet exists in the dicionary. This is thus shorter and more efficient than first checking if the key exists yourself since Python does not have to perform two lookups.
EDIT: Since python-3.7 dictionaries are ordered in insertion order (in the CPython version of python-3.6 that was already the case, but seen as an "implementation detail"). Since python-3.7, you can thus sort the dictionaries with:
bookings = dict(sorted(bookings.items()))
Prior to python-3.7, you can use an OrderedDict [Python-doc]:
from collections import OrderedDict
bookings = OrderedDict(sorted(bookings.items()))
Related
Context I get different values for datetime field when I access them differently. I am sure there is some utc edge magic going on here.
(Pdb++)
Foo.objects.all().values_list('gated_out__occurred__date')[0][0]
datetime.date(2021, 9, 9)
(Pdb++) Foo.objects.all()[0].gated_out.occurred.date()
datetime.date(2021, 9, 10)
Edit: They have the same PK
Foo.objects.all().order_by("pk")[0].gated_out.occurred.date()
datetime.date(2021, 9, 10)
(Pdb++) Foo.objects.all().order_by("pk").values_list('gated_out__occurred__date')[0][0]
datetime.date(2021, 9, 9)
How do I fix/figure out what is happening?
I want to join the sum of related values from users with the users that do not have those values.
Here's a simplified version of my model structure:
class Answer(models.Model):
person = models.ForeignKey(Person)
points = models.PositiveIntegerField(default=100)
correct = models.BooleanField(default=False)
class Person(models.Model):
# irrelevant model fields
Sample dataset:
Person | Answer.Points
------ | ------
3 | 50
3 | 100
2 | 100
2 | 90
Person 4 has no answers and therefore, points
With the query below, I can achieve the sum of points for each person:
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Sum('answer__points')).\
values('pk', 'points')
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}]>
But, since some people might not have any related Answer entries, they will have 0 points and with the query below I use Coalesce to "fake" their points, like so:
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
<QuerySet [{'pk': 4, 'points': 0}]>
Both of these work as intended but I want to have them in the same queryset so I use the union operator | to join them:
everyone = people_with_points | people_without_points
Now, for the problem:
After this, the people without points have their points value turned into None instead of 0.
<QuerySet [{'pk': 2, 'points': 190}, {'pk': 3, 'points': 150}, {'pk': 4, 'points': None}]>
Anyone has any idea of why this happens?
Thanks!
I should mention that I can fix that by annotating the queryset again and coalescing the null values to 0, like this:
everyone.\
annotate(real_points=Concat(Coalesce(F('points'), 0), Value(''))).\
values('pk', 'real_points')
<QuerySet [{'pk': 2, 'real_points': 190}, {'pk': 3, 'real_points': 150}, {'pk': 4, 'real_points': 0}]>
But I wish to understand why the union does not work as I expected in my original question.
EDIT:
I think I got it. A friend instructed me to use django-debug-toolbar to check my SQL queries to investigate further on this situation and I found out the following:
Since it's a union of two queries, the second query annotation is somehow not considered and the COALESCE to 0 is not used. By moving that to the first query it is propagated to the second query and I could achieve the expected result.
Basically, I changed the following:
# Moved the "Coalesce" to the initial query
people_with_points = Person.objects.\
filter(answer__correct=True).\
annotate(points=Coalesce(Sum('answer__points'), 0)).\
values('pk', 'points')
# Second query does not have it anymore
people_without_points = Person.objects.\
exclude(pk__in=people_with_points.values_list('pk')).\
values('pk', 'points')
# We will have the values with 0 here!
everyone = people_with_points | people_without_points
I a writing an inline_script in open sesame (python).
Can anyone tell me what's wrong here? (i think its something very simple, but i can not find it)
when i put the number in List = [1,2,3,4,5,6,7] the first line works, but the second does not work :(
BalanceList1 = range(1:7) + range(13:19) #does not work
if self.get('subject_nr') == "BalanceList1":
#here follows a list of commands
BalanceList2 = list(range(7:13))+list(range(19:25)) #does not work either
elif self.get('subject_nr') == "BalanceList2":
#other commands
In python 2.x you can do the following:
BalanceList1 = range(1,6) + range(13,19)
which will generate 2 lists and add them together in BalanceList1:
[1, 2, 3, 4, 5, 13, 14, 15, 16, 17, 18]
In python 3.x, range doesn't return a list anymore but an iterator (and xrange is gone), you have to explicitly convert to list:
BalanceList1 = list(range(1,6))+list(range(13,19))
A more optimal way to avoid creating too many temporary lists would be:
BalanceList1 = list(range(1,6))
BalanceList1.extend(range(13,19)) # avoids creating the list for 13->18
more optimal than:
I have a model with test data as below
id days
1, 30
1, 40
2, 10
2, 20
1, 90
I want output as
1, [30,40,90]
2, [10,20]
How can I get this in Django?
It's not much Django, it's pure python. To get the result as a mapping on 'id' as key:
result = {}
for obj in Mymodel.objects.all():
if result.has_key(obj.id):
result[obj.id].append(obj.days)
else:
result[obj.id] = [obj.days]
print result
>>> {1: [30, 40, 90], 2: [10, 20]}
The order of the elements in each list is not defined. If you require these to be ordered, best would be to append .order_by('days') on the Queryset.
A final remark: Your 'id' is not unique. I would consider a non-pk-column named 'id' a bad practice, since 'id' is Django's default name for the automatically created pk-field.
I'm trying to fill out the registration for a website with python mechanize. Everything is going well but I can't figure out how to do the select controls. For example, if I'm picking my birthday month, here's the form that I need to fill out:
<SelectControl(mm=[*, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])>
I've viewed all the answers on stackoverflow already and they all seem to be some variation like this:
br.find_control(name="mm").value = ["0"]
or
form["mm"] = ["1"]
The problem here is that it gives me a error ItemNotFoundError: insufficient items with name '0'
item = br.find_control(name="mm" type="select").get("12")
item.selected = True
Nvm I just needed to do br.form['mm'] = ["1"] <--- I selected this but could have picked any of the values they allowed.
I have used all of the following:
br['mm'] = ['9']
br['mm'] = ['9',]
br.form['mm'] = ['9']
br.form['mm'] = ['9',]
I seem to remember one case where the comma was mandatory.