MultipleObjectsReturned: get () вернул более одного драйвера - он вернул 3 - PullRequest
0 голосов
/ 30 апреля 2020

У меня есть API-интерфейс DRF, который должен поддерживать приложение Android и IOS. У API есть три типа пользователей, а именно: Драйвер, Клиент и Администратор.

Клиент должен создать запись о бронировании и автоматически назначить водителю API. В зависимости от наличия дней и времени. В настоящий момент каждый день (Вс, Пн, Вт, Ср, Чт, Пт) является рабочим днем, и продолжительность рабочего дня колеблется с 7:00 до 21:00, а рабочее время, включая переход, составляет один час. Например, бронирование в 9:00 утра займет час, следовательно, время окончания будет 10:00, все факторы включительно. Многие водители могут работать одновременно. Приложение должно дать отзыв пользователю, если он попытается забронировать уже выбранные слоты.

Моя проблема в настоящее время состоит в том, чтобы l oop через уже существующих водителей из таблицы водителей, оставьте их присоединенными к таблице бронирования и и назначьте их один за другим. Я смог назначить одного водителя на работу. Но когда я добавил драйверы, стало трудно. Есть что-то, что я не поправляю, и я не знаю, что это такое.

Вот мои модели.

""" models.py """


    """ Helper function to check overlapping times """
    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

    """ Function to check overlapping bookings """
    def overlapping_bookings(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.overlapping_bookings()
        super(Booking, self).save(*args, **kwargs)

Из вышеупомянутых моделей я написал функцию для проверки перекрытия время для того же времени booking_date, booking_time и finish_at. Это вдохновлено ALEXANDRE PINTO on> https://alexpnt.github.io/2017/07/15/django-calendar/

Ниже приведены сериализаторы



# BOOKING SERIALIZER
# ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––#


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__'
        exclude = ('driver',)
        read_only_fields = (
            'booking_total', 'booking_status',
            'booked_at', 'finishing_at', 'client'
        )

    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)

    """ Function to create booking based on authenticated client and available drivers """
    def create(self, validated_data):
        validated_data['client'] = self.context['request'].user
        validated_data['driver'] = Driver.objects.get(active=True)

        bookings = Booking.objects.create(**validated_data)
        return bookings

Вот журналы моего сервера:

Журнал сервера

1 Ответ

0 голосов
/ 03 мая 2020

----------------------------------------------- -------------------------

Ниже представлено решение, которое я создал для решения следующей проблемы. Любой может это усовершенствовать. Примечание: он получает помощь от функции overlapping_bookings() для того, чтобы больше не останавливать бронирования на одну дату и время, когда все драйверы распределены. Это приводит к ошибке ValidationError от overlapping_bookings() на save()

Я надеюсь, что это поможет кому-то

В serializers.py функция переопределяет ModelViewSet create.

def create(self, validated_data):
        """ Function to create booking objects \
        and allocate drivers automatically. """

        validated_data['client'] = self.context['request'].user

        """ Variable to save all drivers (querysets) from the driver \
        table ordered by the booking date and time. --> maybe there is \
        a better way to do it. Avoid using .get()"""

        drivers = Driver.objects.filter(active=True).order_by(
            '-booking__booking_date', '-booking__booking_time').all()

        """ Check whether the drivers querysets (list) exists """
        if drivers.exists():

            """ For loop to isolate a single query set from list of \
            quersets (drivers) """
            for drv in drivers:

                """ Condition to check for inner join query between \
                driver and booking table carefully filtering them using \
                booking_date and booking_time. This code is helped by \
                the clean() function in models. Which after every active=True \
                driver is allocated a booking, raises a ValidationError. \

                It is subject to be made better. Trying to find out how it \
                will throw a HttpResponse error instead."""

                if Booking.objects.select_related('driver').filter(
                    booking_date=validated_data['booking_date'],
                    booking_time=validated_data['booking_time'],
                ).annotate(drv=F('driver__user_ptr')).exists():
                    continue
            try:
                return Booking.objects.create(driver=drv, **validated_data)
            except Booking.DoesNotExist:
                pass
...