Skip to content

Instantly share code, notes, and snippets.

@pmburu
Last active May 2, 2020 16:57
Show Gist options
  • Save pmburu/3ce796b239f91b6f0f3f66c70e8464d5 to your computer and use it in GitHub Desktop.
Save pmburu/3ce796b239f91b6f0f3f66c70e8464d5 to your computer and use it in GitHub Desktop.
MultipleObjectsReturned at /api/v1/bookings/booking/
get() returned more than one Driver -- it returned 3!
from django.urls import reverse
from django.db import models
from django.utils import timezone
from django.core.exceptions import ValidationError
# import datetime as dt
# from django.db.models.signals import pre_save, post_save, post_delete
import datetime
from wash_authentication.models import Client, Driver
# SERVICES
# _________________________________________________________________________#
class VehicleType(models.Model):
""" Represents vehicle categories """
name = models.CharField(max_length=30, help_text='E.g. SUV')
parent = models.ForeignKey('VehicleType', on_delete=models.CASCADE,
null=True, blank=True)
created = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.name
class PackageDetails(models.Model):
""" Represents vehcile details """
vehicle_category = models.ForeignKey(
VehicleType, on_delete=models.CASCADE, related_name='wash_package',
)
package_name = models.CharField(max_length=30, help_text='E.g. Deluxe')
package_description = models.TextField(null=True, blank=True)
package_cost = models.DecimalField(max_digits=10, decimal_places=2)
created = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.package_name
def get_absolute_url(self):
return reverse("product_detail", kwargs={"pk": self.pk})
def get_price(self):
return self.package_cost
class ExtraService(models.Model):
""" Represents extra services details """
service_name = models.CharField(max_length=30,
help_text='E.g. Engine Wash')
service_cost = models.DecimalField(max_digits=10, decimal_places=2)
# BOOKINGS
# _________________________________________________________________________#
class AllocatedBooking(models.Model):
allocated_booking_id = models.PositiveIntegerField()
allocated_booking_date = models.DateField()
allocated_booking_time = models.TimeField()
allocated_driver_id = models.PositiveIntegerField()
def __str__(self):
return str(self.id)
class BookingState(models.TextChoices):
PENDING = 'PENDING', 'Pending'
CONFIRMED = 'CONFIRMED', 'Confirmed'
ON_THE_WAY = 'ON_THE_WAY', 'On The Way'
IN_PROGRESS = 'IN_PROGRESS', 'In Progress'
COMPLETE = 'COMPLETE', 'Complete'
class Booking(models.Model):
driver = models.ForeignKey(Driver, on_delete=models.CASCADE,
null=True, blank=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE,
related_name='customer')
booking_location = models.CharField(max_length=250, blank=True, null=True)
booking_address = models.CharField(max_length=100, blank=True, null=True)
booking_date = models.DateField()
booking_time = models.TimeField()
service = models.ForeignKey(PackageDetails,
on_delete=models.CASCADE,
related_name='packages')
extra_service = models.ForeignKey(ExtraService, on_delete=models.CASCADE,
related_name='extra_services',
null=True, blank=True)
booking_status = models.CharField(
max_length=15, choices=BookingState.choices,
default=BookingState.PENDING
)
booked_at = models.DateTimeField(default=timezone.now)
finishing_at = models.TimeField()
booking_total = models.DecimalField(decimal_places=2, max_digits=10)
def __str__(self):
return str(self.id)
def calc_finishing_at(self):
duration = datetime.timedelta(hours=1)
booking_time = self.booking_time
self.finishing_at = (datetime.datetime.combine(
datetime.date(1, 1, 1), booking_time) + duration).time()
return self.finishing_at
def calc_booking_total(self):
if self.extra_service and self.service:
self.booking_total = self.service.package_cost + \
self.extra_service.service_cost
elif self.service:
self.booking_total = self.service.package_cost
else:
self.booking_total = 0.00
def check_time_overlap(self, fixed_start, fixed_end, new_start, new_end):
time_overlap = False
if new_start == fixed_end or new_end == fixed_start: # edge case
time_overlap = False
elif (new_start >= fixed_start and new_start <= fixed_end) or \
(new_end >= fixed_start and new_end <= fixed_end): \
# innner limits
time_overlap = True
elif new_start <= fixed_start and new_end >= fixed_end: \
# outter limits
time_overlap = True
return time_overlap
def assign_driver_to_booking(self):
if self.finishing_at <= self.booking_time:
raise ValidationError(
'Finishing times must be after booking times'
)
bookings = Booking.objects.filter(
booking_date=self.booking_date, driver_id=self.driver
)
if bookings.exists():
for booking in bookings:
""" Check whether date and time overlaps """
if self.check_time_overlap(
booking.booking_time, booking.finishing_at,
self.booking_time, self.finishing_at
):
""" If all drivers are allocated, raise an error \
message. """
raise ValidationError(
'All our drivers are booked at: ' +
str(booking.booking_date) + ', ' + str(
booking.booking_time) + '-' +
str(booking.finishing_at))
def save(self, *args, **kwargs):
self.calc_booking_total()
self.calc_finishing_at()
# self.assign_driver_to_booking()
super(Booking, self).save(*args, **kwargs)
[
{
"id": 143,
"package": "Premium",
"vehicle_type": "SUV",
"client_name": "Test Client",
"client_phone": "0722345678",
"booking_location": "-538978, 56251817",
"booking_address": "ABC Place, Hse 41",
"booking_date": "2020-05-28",
"booking_time": "13:00:00",
"booking_status": "PENDING",
"booked_at": "2020-05-01T11:49:40.219171",
"finishing_at": "14:00:00",
"booking_total": "3700.00",
"driver": 4,
"client": 3,
"service": 8,
"extra_service": 3
}
]
from django.db.models import F
from django.db.models import Q
from django.db import connection
from rest_framework import serializers
from .models import (
Booking, PackageDetails, VehicleType, ExtraService, BookingState,
AllocatedBooking
)
from wash_authentication.models import (
Client, Driver
)
# from pushy.utils import send_push_notification
# from pushy.models import Device
class PackageSerializer(serializers.ModelSerializer):
package_id = serializers.IntegerField(source='id')
class Meta:
model = PackageDetails
fields = (
'package_id',
'package_name',
'package_description',
'package_cost',
)
class VehicleTypeSerializer(serializers.ModelSerializer):
wash_package = PackageSerializer(many=True, read_only=True)
class Meta:
model = VehicleType
fields = (
'id',
'name',
'wash_package'
)
class ExtraServiceSerializer(serializers.ModelSerializer):
extra_service_id = serializers.IntegerField(source='id')
class Meta:
model = ExtraService
fields = (
'extra_service_id',
'service_name',
'service_cost',
)
# BOOKING SERIALIZER
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––#
class BookingClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ("id", "name", "phone")
class BookingDriverSerializer(serializers.ModelSerializer):
class Meta:
model = Driver
fields = ("id", "name", "phone")
class BookingSerializer(serializers.ModelSerializer):
package = serializers.SerializerMethodField()
vehicle_type = serializers.SerializerMethodField()
client_name = serializers.SerializerMethodField()
client_phone = serializers.SerializerMethodField()
class Meta:
model = Booking
fields = '__all__'
read_only_fields = (
'booking_total',
'booked_at', 'finishing_at', 'client', 'driver'
)
write_only_fields = ('booking_status',)
def get_fields(self, *args, **kwargs):
fields = super(BookingSerializer, self).get_fields(*args, **kwargs)
request = self.context.get('request', None)
if request and getattr(request, 'method', None) == "POST":
fields['booking_status'].required = False
return fields
def get_package(self, obj):
return obj.service.package_name
def get_vehicle_type(self, obj):
return obj.service.vehicle_category.name
def get_client_name(self, obj):
return obj.client.name
def get_client_phone(self, obj):
return str(obj.client.phone)
# THINGS I WAS TRYING BEFORE!! UNTIL I WENT TO MANIPULATE THEM ON THE VIEWSETS
# def check_driver_in_booking():
# drivers = Booking.objects.select_related('driver').annotate(
# drive_id=F('driver__user_ptr')
# ).all()
# #
# # print(drivers)
# # check_driver_in_booking()
# booking_arr = []
# for single_driver in drivers:
# booking_arr.append({
# 'allocated_booking_id': single_driver.id,
# 'allocated_booking_date': single_driver.booking_date,
# 'allocated_driver_id': single_driver.drive_id,
# 'allocated_booking_time': single_driver.booking_time,
# # 'client': single_driver.client.user_ptr_id
# })
#
# for allocated in booking_arr:
# my_list = []
#
# my_list.append(allocated)
#
# for my_dict in my_list:
# cursor = connection.cursor()
# placeholders = ', '.join(['%s'] * len(my_dict))
# columns = ', '.join("`" + str(x).replace('/', '_') +
# "`" for x in my_dict.keys())
# values = ', '.join("'" + str(x).replace('/', '_') +
# "'" for x in my_dict.values())
# sql = "INSERT INTO %s ( %s ) VALUES ( %s )" % (
# 'wash_bookings_allocatedbooking', columns, values, placeholders)
#
# # sql = f"INSERT INTO wash_bookings_allocatedbooking \
# # ({', '.join(my_dict.keys())})"
# # sql += f"VALUES ({', '.join(my_dict.keys())})"
# cursor.execute(sql, my_dict)
#
# rows = cursor.fetchall()
# # return rows
# print(rows)
# columns = ', '.join("`" + str(x).replace('/', '_') +
# "`" for x in allocated.keys())
# values = ', '.join("'" + str(x).replace('/', '_') +
# "'" for x in allocated.values())
# sql = "INSERT INTO %s ( %s ) VALUES ( %s );" %[
# 'wash_bookings_allocatedbooking', columns, values]
# cursor.execute(sql, allocated.values())
# check_driver_in_booking()
# MyModel.objects.bulk_create(objects)
#
# for single_booking in booking_arr:
# for b_key, b_val in single_booking.items():
# if b_key == 'drive_id':
# return b_val
# # return single_booking
#
# def check_driver_in_drivers():
# driver_arr = []
# all_drivers = Driver.objects.filter(active=True)
#
# for a_driver in all_drivers:
# driver_arr.append({
# 'drive_id': a_driver.user_ptr_id,
# })
# for single_driver in driver_arr:
# single_driver_arr = []
# for d_key, d_val in single_driver.items():
# if d_key == 'drive_id':
# # continue
# single_driver_arr.append(d_val)
# print(single_driver_arr)
# check_driver_in_booking()
# def comparison(self):
# self.check_driver_in_booking():
# continue
# def get_driver(self, booking):
# qs = Driver.objects.filter(booking__isnull=True, booking=booking)
# serializer = BookingDriverSerializer(instance=qs, many=True)
# return serializer.data
# validated_data['driver'] = Driver.objects\
# .filter(
# Q(booking__booking_date__isnull=True) &
# Q(booking__booking_time__isnull=True)
# )
# def create(self, validated_data):
# validated_data['client'] = self.context['request'].user
#
# # validated_data['driver'] = Driver.objects.filter(active=True).last()
# bookings = Booking.objects.all()
#
# for booking in bookings:
# if booking.booking_date.exists() and booking.booking_time.exists():
# <QuerySet [<Booking: 143>, <Booking: 146>]>
# bookings = Booking.objects.create(**validated_data)
# driver_a = Driver.objects.filter(active=True).first()
# driver_b = Driver.objects.filter(active=True).last()
# bookings = Booking.objects.all()
# for booking in bookings:
# if driver_a.user_ptr == booking.driver_id:
# continue
# validated_data['driver'] = driver_b
# booking = Booking.objects.create(**validated_data)
# print(bookings)
# return booking
# validated_data['driver'] = driver_a and driver_b
# validated_data['driver'] = Driver.objects.filter(active=True).last()
# bookings = Booking.objects.create(**validated_data)
# return bookings
# validated_data['driver'] = Driver.objects.get(active=True)
# validated_data['driver'] = Driver.objects.filter(active=True) -->>>>
# occupied_drivers = Booking.objects.select_related('driver').filter(
# driver__isnull=False,
# ).all()
# if occupied_drivers.exists():
# for occupied_driver in occupied_drivers:
# drivers = Driver.objects.get(active=True)
# if drivers in occupied_driver.
# not_occupied_drivers = Booking.objects.filter(driver__in=F('driver'))
# print(occupied_drivers)
# bookings = Booking.objects.filter(
# booking_date=self.booking_date,
# driver_id=self.driver_id,
# booking_time=self.booking_time
# )
#
# if bookings.exists():
# for booking in bookings:
# driver = Driver.objects.get(active=True)
# if driver == booking.drive_id:
# continue
# print(driver)
# if self.check_time_overlap(
# booking.booking_time, booking.finishing_at,
# self.booking_time, self.finishing_at
# ):
# """ If all drivers are allocated, raise an error \
# message. """
# raise ValidationError(
# 'All our drivers are booked at: ' +
# str(booking.booking_date) + ', ' + str(
# booking.booking_time) + '-' +
# str(booking.finishing_at))
# .first() or Driver.objects.exclude(active=False).last()
# validated_data['driver'] = Driver.objects.exclude(active=False)\
# .last()
# validated_data['driver'] = Driver.objects.filter(active=True)[0:1]\
# .get()
# validated_data['driver'] = Driver.objects.select_related('booking')\
# .exclude(
# user_ptr='booking__driver'
# ).get(active=True)
# if single_driver in validated_data['driver']:
# break
# bookings = Booking.objects.create(**validated_data)
# print(bookings)
# for dr_bk in validated_data['driver']:
# if dr_bk.booking__id == validated_data.get['id'] and \
# dr_bk:
# continue
# bookings = Booking.objects.create(**validated_data)
# return bookings
# if bookings:
# device = Device.objects.get()
# send_push_notification(
# 'New Booking!',
# payload={'body': 'Please check the new booking'},
# device=device, store=True
# )
# return bookings
# {'id': 143, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 4, 'booking_time': datetime.time(13, 0), 'client': 3}
# {'id': 149, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 4, 'booking_time': datetime.time(15, 0), 'client': 3}
# {'id': 146, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(13, 0), 'client': 3}
# {'id': 150, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(15, 0), 'client': 3}
# {'id': 151, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': 6, 'booking_time': datetime.time(18, 0), 'client': 3}
# {'id': 152, 'booking_date': datetime.date(2020, 5, 28), 'drive_id': None, 'booking_time': datetime.time(18, 0), 'client': 3}
# import json
from django.db.models import Q
# from django.utils import timezone
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view
from rest_framework import viewsets
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
# from rest_framework.decorators import permission_classes
# from .permissions import IsDriver
from wash_authentication.models import (
Client, Driver
)
from .serializers import (
PackageSerializer, VehicleTypeSerializer, ExtraServiceSerializer,
BookingSerializer
)
from .models import (
PackageDetails, VehicleType, ExtraService, Booking, BookingState
)
# PACKAGE & SERVICE VIEWS
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––#
class PackageViewSet(viewsets.ModelViewSet):
serializer_class = PackageSerializer
permission_classes = (IsAuthenticated,)
def get_queryset(self):
return PackageDetails.objects.filter(
pk=self.kwargs['pk'])
class VehicleTypeViewSet(viewsets.ModelViewSet):
queryset = VehicleType.objects.all()
serializer_class = VehicleTypeSerializer
permission_classes = (IsAuthenticated,)
class ExtraServiceViewSet(viewsets.ModelViewSet):
queryset = ExtraService.objects.all()
serializer_class = ExtraServiceSerializer
permission_classes = (IsAuthenticated,)
# BOOKING & CART VIEWS
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––#
""" This class facilitates client booking and retrieval views """
class BookingViewSet(viewsets.ModelViewSet):
serializer_class = BookingSerializer
permission_classes = (IsAuthenticated,)
queryset = Booking.objects.all()
def get_queryset(self):
request = self.request
qs = Booking.objects.select_related('client').prefetch_related(
'service').filter(client=request.user.client)\
.order_by('-booking_date', '-booking_time')
return qs
def perform_create(self, serializer):
client = get_object_or_404(Client,
id=self.request.user.user_ptr_id)
drivers = Driver.objects.get(active=True)
# bookings = Booking.objects.get().order_by('-booking_date',
# 'booking_time',
# 'driver')
if drivers.exists():
for drv in drivers:
if drv.user_ptr_id in Booking.objects\
.get(
booking_date=self.request.data['booking_date'],
booking_time=self.request.data['booking_time']
):
continue
try:
instance, created = Booking.objects\
.get_or_create(driver=drv)
serializer.save(client=client, driver=drv)
except Driver.DoesNotExist:
# serializer.save(client=client, driver=drv)
return Response('All our drivers are currently booked')
# def perform_create(self, serializer):
# queryset = SignupRequest.objects.filter(user=self.request.user)
# if queryset.exists():
# raise ValidationError('You have already signed up')
# serializer.save(user=self.request.user)
# def dispatch(self, *args, **kwargs):
# response = super().dispatch(*args, **kwargs)
#
# return response
class DriverBookingList(generics.ListAPIView):
"""
List all driver bookings, or create a new snippet.
"""
permission_classes = [IsAuthenticated]
queryset = Booking.objects.all()
serializer_class = BookingSerializer
def get_queryset(self):
request = self.request
qs = Booking.objects.filter(
Q(booking_status=BookingState.PENDING) |
Q(booking_status=BookingState.IN_PROGRESS),
driver=request.user.driver
).order_by("-id")
return qs
class DriverBookingDetails(generics.RetrieveUpdateAPIView):
"""
Retrieve all driver bookings, and partial update on status.
"""
permission_classes = [IsAuthenticated]
queryset = Booking.objects.all()
serializer_class = BookingSerializer
def get_queryset(self):
request = self.request
qs = Booking.objects.filter(
Q(booking_status=BookingState.PENDING) |
Q(booking_status=BookingState.IN_PROGRESS),
driver=request.user.driver
).order_by("-id")
return qs
def get_serializer(self, *args, **kwargs):
kwargs['partial'] = True
# kwargs['get'] = True
return super(DriverBookingDetails, self)\
.get_serializer(*args, **kwargs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment