cannot unpack non-iterable datetime.date object - python

I'm building a booking form, and want to allow users to pick a date of booking from available dates in the next 60 days.
I get the next 60 days by:
base = datetime.date.today()
date_list = [base + datetime.timedelta(days=x) for x in range(60)]
Then I subtract already booked dates which are stored in the db:
bookings = list(Booking.objects.all())
primarykeys = []
unav = []
for b in bookings:
primarykeys.append(b.pk)
for p in primarykeys:
unav.append(Booking.objects.get(pk=p).booking_date)
for date in unav:
if date in date_list:
date_list.remove(date)
Then I change the result into tuple for the forms(not sure if this is right?):`
date_list = tuple(date_list)
Then I pass it into the form field as such:
booking_date = forms.ChoiceField(choices=date_list, required=True)
This gives me an error of cannot unpack non-iterable datetime.date object
And now am I stumped...how can I do this? I have a feeling i'm on the complete wrong path.
Thanks in advance

The docs for Django Form fields says the following:
choices
Either an iterable of 2-tuples to use as choices for this field, or a callable that returns such an iterable. This argument accepts the
same formats as the choices argument to a model field. See the model
field reference documentation on choices for more details. If the
argument is a callable, it is evaluated each time the field’s form is
initialized. Defaults to an empty list.
It looks like what you're passing is a tuple in this format:
(date object, date object, ...)
But you need to be passing something like a list of 2-tuples, with the first element of each tuple being the value stored for each choice, and the second element being the value displayed to the user in the form:
[(date_object, date_string), (date_object, date_string), ...)
Change your code to the following and see if that works for you:
base = datetime.date.today()
date_set = set([base + datetime.timedelta(days=x) for x in range(60)])
booking_dates = set(Booking.objects.all().values_list('booking_date', flat=True))
valid_dates = date_set - booking_dates
date_choices = sorted([(valid_date, valid_date.strftime('%Y-%m-%d')) for valid_date in valid_dates],
key=lambda x: x[0])
I've used sets to make it simpler to ensure unique values and subtract the two from each other without multiple for loops. You can use values_list with flat=True to get all the existing booking dates, then create a list of 2-tuples date_choices, with the actual datetime object as the value and display a string representation in whatever format you choose using strftime.
Then the dates are sorted using sorted by date ascending based on the first key, since using sets will mess up the sort order.
Then take a look at this question to see how you can pass these choices into the form from your view, as I don't think it's good to try to dynamically set the choices when defining the Form class itself.

Related

Python/Django: Filter objects by Concat() and order by the same concatenation

In my Python/Django API project I need to get a quesryset from a model filtered by a concatenated combination of two fields and order by the same concatenation. However I only know to filter by one field like shown below.
Requirement: TimeTable has two fields "date" and "time". I need to list data where the combination of both these fields are greater than current date time.
Code:
current_time = datetime.now()
timetable = TimeTable.objects.filter(user=user_id, date__gte=current_time.date()).order_by(Concat('date','time'))
MySQL equivalent of the scenario:
SELECT * FROM time_table where concat(date,' ',time)>='2020-03-24 12:00:00' and user=user_id order by concat(date,' ',time) ASC
How can I accomplish this?
Just provide multiple fields in the order_by(...) function as,
TimeTable.objects.filter(date__gte='2020-04-02').order_by('date','time')
Note: You don't have to concatinate those values (in your case).
Update
from django.db.models.functions import Concat, Cast
from django.db.models import DateTimeField, CharField, Value
from datetime import datetime
date_time_expr = Cast(Concat('date', Value(' '), 'time', output_field=CharField()), output_field=DateTimeField())
TimeTable.objects.annotate(date_time=date_time_expr).filter(date_time__gte=datetime.now()).order_by('date_time')

Django groupby day fill missing data with 0

I want to make a request in Django where I group by day but I want to fill the day where there is no result with 0, is it possible?
# I use the following query
AccessLog
.objects
.filter(attempt_time__gte=last_30_days)
.annotate(day=TruncDay('attempt_time'))
.values('day', 'username')
.annotate(c = Count('username'))
.order_by('day')
No, it is not possible with annotations. Annotations work with the similar types, for example, Coalesce function requires similar types and mixing datetime and numbers will result in a database error. The same for the Case function there is only one output field per result.
The function TruncDay returns a DateTime (in this case) with fields up to Day set to their minimum value, so for instance 2015-06-15 14:30:50.000321+00:00 will be converted to 2015-01-01 00:00:00+00:00 how documentation outlines. And actually annotated value cannot be sometimes integer and sometimes datetime object.
Occasionally to denote that the values are "None" in such situations preferable way would be to set it to the minimal/maximum value (we assume that the value cannot be equal to it), for instance:
AccessLog.objects.filter(
attempt_time__gte=last_30_days
).annotate(
day=Coalesce(TruncDay('attempt_time'), datetime.min)
).values('day', 'username').annotate(
c=Count('username')
).order_by('day')

Get object with specific hour in django

I have model like this:
class Order(models.Model):
dateTime = models.DateTimeField()
and I want to get object with specific hour
how can I do that?
the code below doesn't work:
o=Order.objects.get(dateTime.hour=12)
and has this problem: keyword can't be an expression
now.. How should I give the order object with specific time?
The following will give you all the objects having hour value as 12.
o = Order.objects.filter(dateTime__hour=12)
which can be used in place of
o = Order.objects.get(dateTime__hour=12)`
to get that one object, in case you have unique hour values for objects.
But if already know that you have unique value of hour then you should use
the later.
https://docs.djangoproject.com/en/1.9/ref/models/querysets/#hour
o = Order.objects.filter(dateTime__hour=12)

Comparing django.forms.fields.DateField with datetime.date

I have a formset that I initialize with some values, one which is a datetime.date. I want to loop through the list and find a day and disable it, however the comparison on line 3 fails as one django DateField and the other is a datetime.date.
def disable_day(formset, disableddate):
for formelement in formset.forms:
if disableddate == formelement.fields['date']: # This comparison fails
formelement.fields["somefield"].widget.attrs['readonly'] = True
How do I convert or otherwise compare these two different types?
Instead of comparing DateField and datetime objects, you should compare cleaned_data values with datetime. Something like this should work:
def disable_day(formset, disableddate):
for formelement in formset.forms:
if formelement.is_valid():
if disableddate == formelement.cleaned_data['date']:
formelement.fields["somefield"].widget.attrs['readonly'] = True

Preserving the right order of Sphinx results when passing them to a table queryset

Sphinx returns results as a dictionary. For example, the sequence of resulted IDs is [3,1,5,2,4] ordered by sphinx field weights. When passing them to a table object as a QuerySet I get table with objects sorted by IDs like [1,2,3,4,5]. How to preserve the right order of items in a queryset?
result = c.Query(q, 'db')
ids = [obj['id'] for obj in result['matches']]
queryset = Model.objects.filter(id__in=ids)
table = Table(queryset)
I can get rid of using tables at all, but it takes too long to execute something like
objects = [Model.objects.get(pk=obj['id']) for obj in result['matches']]
How it can be optimised?

Categories

Resources