1

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"

Community
  • 1
  • 1
Kevin D.
  • 315
  • 2
  • 19
  • Possible duplicate of [Iterating through a range of dates in Python](https://stackoverflow.com/questions/1060279/iterating-through-a-range-of-dates-in-python) – dirkgroten Aug 06 '19 at 09:01
  • Not entirely sure, it didn't really help me. The question here is how we can find missing dates and add default values to a dictionary. – Kevin D. Aug 06 '19 at 12:37
  • But if you can iterate through a list of dates as shown in the duplicate (which is what you’re doing wrong), you can check for each date if it’s contained in the dictionary and assign it a value if not. As you do now but with a different for loop. – dirkgroten Aug 06 '19 at 12:54
  • `seven_days` is a date that’s why you get the error. You just need to replace it with a range of dates. – dirkgroten Aug 06 '19 at 12:56

1 Answers1

1

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 dictionaries are ordered in insertion order (in the CPython version of that was already the case, but seen as an "implementation detail"). Since , you can thus sort the dictionaries with:

bookings = dict(sorted(bookings.items()))

Prior to , you can use an OrderedDict [Python-doc]:

from collections import OrderedDict

bookings = OrderedDict(sorted(bookings.items()))
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Well thanks again, although this just adds the default values at the end of the dictionary. They still need to be sorted afterwards before being used as graphdata. – Kevin D. Aug 06 '19 at 09:27
  • @KevinD.: dictionaries use *insertion order* since Python-3.6. Before these could have *any* order (well you could see these as shuffled), so yes, you probably need to do a post-processing step then. – Willem Van Onsem Aug 06 '19 at 09:28
  • Just beat me to it, does this work on python 3.6? I created an ordered list and then used OrderedDict to convert it back to a dictionary. – Kevin D. Aug 06 '19 at 09:53
  • @KevinD.: by converting an `OrderedDict` back to a dictionary, the order might be lost in Python-3.6, so no, then you need to keep it an `OrderedDict`. – Willem Van Onsem Aug 06 '19 at 09:55