Hello everyone
I have written a query set to generate doctors’ appointment time slots using the Postgres generate_series() function like this:
generate_series_func = Func(
F('date') + F('schedule__start_at'),
F('date') + F('schedule__end_at'),
F('doctor__appointments_gap'),
function='generate_series',
output_field=DateTimeField()
)
queryset = Appointment.objects.filter(pk=pk).only(
'date',
'doctor__appointments_gap',
'schedule__start_at',
'schedule__end_at',
'unavailable_time_slot__from_time',
'unavailable_time_slot__to_time',
)\
.annotate(time_slot=Cast(generate_series_func, output_field=TimeField()))\
.alias(time_slot_alias=F('time_slot'))\
.exclude(time_slot_alias__gt=F('unavailable_time_slot__from_time'))\
.all()
Everything is ok except that when I try to filter and exclude unavailable time slots, I get this error:
set-returning functions are not allowed in WHERE
LINE 1: ...ERE ("appointment_appointment"."id" = 1 AND NOT ((generate_s...
This is because Django repeats the “generate_series()” function call in the evaluated SQL query like this:
SELECT "appointment_appointment"."id",
"appointment_appointment"."doctor_id",
"appointment_appointment"."date",
(generate_series(("appointment_appointment"."date" + "appointment_appointmentschedule"."start_at"), ("appointment_appointment"."date" + "appointment_appointmentschedule"."end_at"), "account_doctor"."appointments_gap"))::time AS "time_slot"
FROM "appointment_appointment"
LEFT OUTER JOIN "appointment_appointmentschedule"
ON ("appointment_appointment"."id" = "appointment_appointmentschedule"."appointment_id")
INNER JOIN "account_doctor"
ON ("appointment_appointment"."doctor_id" = "account_doctor"."user_id")
LEFT OUTER JOIN "appointment_unavailabletimeslot"
ON ("appointment_appointment"."id" = "appointment_unavailabletimeslot"."appointment_id")
WHERE ("appointment_appointment"."id" = 1 AND NOT ((generate_series(("appointment_appointment"."date" + "appointment_appointmentschedule"."start_at"), ("appointment_appointment"."date" + "appointment_appointmentschedule"."end_at"), "account_doctor"."appointments_gap"))::time > ("appointment_unavailabletimeslot"."from_time")))
ORDER BY "appointment_appointment"."date" DESC
Noticed? The “generate_series()” function gets added in the WHERE clause too.
I’ve tried to use the “.alias()” method to save the expression but it got removed from the SELECT statement and appeared in the WHERE clause and I got the same error.
I’ve also tried to use the “.anotate()” method alongside the “.alias()” method to add the time_slot in the query set result and to be able to filter it but again the “generate_series()” function got repeated in the SELECT statement as well as “WHERE” clause.