Как вернуть список доступных временных интервалов через форму ValidationError
models.py
from django.db import models
from django.utils import timezone
from django.urls import reverse
from django.contrib.auth.models import User
class Customer(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=20,null=True)
def __str__(self):
return self.name
# Create your models here.
class Booking(models.Model):
customer_name = models.ForeignKey(Customer,on_delete=models.CASCADE,null=True)
username = models.ForeignKey(User,on_delete=models.CASCADE)
qty_plts = models.PositiveSmallIntegerField(default=1)
cbm = models.PositiveSmallIntegerField(default=1)
created_date = models.DateTimeField(default=timezone.now())
delivery_date = models.DateField(null=True)
delivery_time = models.TimeField(null=True)
booking_number = models.CharField(max_length=50,unique=True)
def __str__(self):
return self.booking_number
def save(self, **kwargs):
if not self.booking_number:
self.booking_number = f"{self.delivery_date:%Y%m%d}{self.delivery_time:%H%M}"
super().save(**kwargs)
def get_absolute_url(self):
return reverse('bookmyslot:detail',kwargs={'pk':self.pk})
forms.py
from django import forms
from bookmyslot.models import Booking,Customer
from bootstrap_datepicker_plus import DatePickerInput
import datetime as dt
from django.utils import timezone
HOUR_CHOICES = [(dt.time(hour=x), '{:02d}:00'.format(x)) for x in range(7, 13)]
class BookingForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
user = kwargs.pop('username',None)
super(BookingForm,self).__init__(*args,**kwargs)
self.fields['qty_plts'].label = "Quantity Of Pallets"
self.fields['cbm'].label = "Shipment CBM"
self.fields['delivery_date'].label = "Delivery Date"
self.fields['delivery_time'].label = "Delivery Time"
self.fields['customer_name'].label = "Customer Name"
self.fields['customer_name'].queryset = Customer.objects.filter(username=user)
def clean(self):
cleaned_data = super(BookingForm,self).clean()
booking_number = f"{cleaned_data.get('delivery_date'):%Y%m%d}{cleaned_data.get('delivery_time'):%H%M}"
if Booking.objects.filter(booking_number=booking_number).exists():
raise forms.ValidationError("Requested slot is already booked, please choose another time")
class Meta:
model = Booking
fields = ('customer_name','qty_plts','cbm','delivery_date','delivery_time')
widgets = {'delivery_date':DatePickerInput(options={"daysOfWeekDisabled":[0,6],"minDate":timezone.now().date().strftime('%Y-%m-%d')}),
'delivery_time':forms.Select(choices=HOUR_CHOICES)}
views.py
from django.shortcuts import render
# Create your views here.
from .models import Booking,Customer
from .forms import BookingForm
from django.urls import reverse,reverse_lazy
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import (ListView,DetailView,CreateView,UpdateView,DeleteView,TemplateView)
class BookingCreate(LoginRequiredMixin,CreateView):
login_url = '/login'
redirect_field_name = 'bookmyslot/booking_detail.html'
model = Booking
form_class = BookingForm
def get_form_kwargs(self, **kwargs):
form_kwargs = super(BookingCreate,self).get_form_kwargs(**kwargs)
form_kwargs['username'] = self.request.user
return form_kwargs
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.username = self.request.user
self.object.save()
return super().form_valid(form)
Я пытаюсь понять, как вернуть список доступных временных интервалов, используя ValidationError. Есть 6 временных слотов [время_доставки] на выбор на любую дату -> [7,8,9,10,11,12]. Поле booking_number - это уникальный идентификатор, представляющий собой конкатенацию delivery_date и delivery_time, который генерируется каждый раз, когда пользователь успешно создает бронирование.
Допустим, есть 3 существующих бронирования на 2021-10-21 на 7:00, 08:00 и 10:00, которые сохранены в модели Booking со следующими номерами бронирования: 202110210700 202110210800 202110211000
Предположим, пользователь пытается забронировать уже существующий слот, например, 202110210700, ошибка валидации должна вернуть "Запрашиваемый слот уже забронирован, пожалуйста, выберите другой из этих доступных слотов": 09:00 11:00 12:00
Как я могу этого достичь?
Для возврата оставшегося временного интервала при валидации формы можно использовать утилитарную функцию вроде этой :
app_name/utilities.py
def list_diff(l1: list, l2: list):
""" Return a list of elements that are present in l1
or in l2 but not in both l1 & l2.
IE: list_diff([1, 2, 3, 4], [2,4]) => [1, 3]
"""
return [i for i in l1 + l2 if i not in l1 or i not in l2]
def check_free_time(time_slot: list, exist_list: list):
""" Return the list of available time slot if exist,
according to a given exist slot list.
Return the remained time slot, or empty list if all are used
IE: ([7, 12], [7, 8, 9, 10, 11, 12]) => [8, 9, 10, 11]
"""
remain_slot = list_diff(time_slot, exist_list)
return remain_slot
Теперь импортируйте check_free_time
в ваш forms.py
файл и используйте его, если booking_number
существует.
from datetime import datetime
from .utilities import check_free_time
if Booking.objects.filter(booking_number=booking_number).exists():
today = datetime.today()
d = today.day
m = today.month
y = today.year
# Retrieve today's bookings
today_bookings = Booking.objects.filter(delivery_date__year=y,delivery_date__month=m delivery_date__day=d)
# A list of today's bookings time slot (take only hours)
# Return something like <QuerySet [{'delivery_date__hour': 11}, ...]>
today_time_slot = today_bookings.values('delivery_date__hour')
# Convert it to list of hours values since the utility function accept list.
today_time_slot_list = [h['delivery_date__hour'] for h in list(today_time_slot)]
# The line above return something like [9, 11, ...]
all_time_slot = [7, 8, 9, 10, 11, 12]
# Now we can call the utility function `check_free_time`
available_slot = check_free_time(all_time_slot, today_time_slot_list)
if available_slot: # The are some available slot (list not empty)
# I use python3.6 f-string to format the message
# Note that the list is in a raw format ([8,11,12]), you can do better like ['8h:00', '11h:00', '12h:00']
message = f"Requested slot is already booked, please choose another time in {available slot}."
raise forms.ValidationError(message)
else: # The list is empty, all slot are taken
message = "The are not available slot for this booking today."
raise forms.ValidationError(message)
NB : Я тестировал только в оболочке python и Django, если есть ошибки, попробуйте добавить в комментарии.