Набор запросов Django ListView для доступных элементов - PullRequest
0 голосов
/ 06 января 2019

Итак, я хочу отобразить все доступные элементы для любой конкретной даты, не должно быть так сложно, но каким-то образом я столкнулся с проблемой, связанной с соответствующими элементами.

Допустим, у нас есть следующие модели, модель для хранения всех заказов и модель с Предметом.

Тогда я бы создал ListView для извлечения всех элементов, доступных между заданными датами. Я перезаписываю набор запросов, чтобы получить данные, заполненные пользователем.

Кажется, что это работает, но есть проблема, хотя я проверяю, не конфликтуют ли "form_start_date" или "form_end_data" с существующими бронированиями, когда один элемент имеет несколько бронирований, он не работает.

пример

Заказы [X] для позиции № 01:
С 01-01-2019 по 01-03-2019
С 01-11-2019 по 01-18-2019

  Jan 2019   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18  
 ---------- --- --- --- --- --- --- --- --- --- ---- ---- ---- ---- ---- ---- ---- ---- ---- 
  Item #01   X   X   X           O   O   O            X    X    X    X    X    X    X    X   
  Item #02       X   X   X   X   X                                                 

Когда я проверяю доступность [O] за период с 01-06-2019 по 01-08-2019, элемент № 01 недоступен, что мне здесь не хватает?

models.py

class Booking(models.Model):
    item = models.ForeignKey('Item', on_delete=models.SET_NULL, null=True)
    start_date = models.DateField()
    end_date = models.DateField()

class Item(models.Model):
    name = models.CharField(max_length=20, unique=True)

views.py

class AvailableItems(generic.ListView):
    model = Item

    def get_queryset(self):
        start_date = datetime.strptime(self.request.GET.get('start_date'), '%Y-%m-%d')
        end_date = datetime.strptime(self.request.GET.get('end_date'), '%Y-%m-%d')
        # As Willem suggested in the comments, it is easier to check for available items
        available_items = (
            Item.objects.filter(booking__start_date__gte = start_date, booking__end_date__lte = end_date)
        )

        if start_date and end_date:
            return available_items
        else:
            return Item.objects.all()

1 Ответ

0 голосов
/ 06 января 2019

Давайте сначала проанализируем, когда два интервала (f 1 , т 1 ) и (f 2 , т 2 ) перекрытие. Проще решить проблему, когда выясняется, когда два интервала не перекрываются. Это верно для двух случаев:

  1. дано t 1 2 , с тех пор первое событие заканчивается раньше второго; или
  2. дано t 2 1 , поскольку первое событие заканчивается до начала второго.

Таким образом, это означает, что два события перекрываются с учетом t 1 & ge; f 2 и t 2 & ge; е 1 .

Обладая этими знаниями, мы можем разработать фильтр как:

bookings = Booking.objects.filter(
    end_date__gte=form_start_date,
    start_date__lte=form_end_date
)

return Item.objects.exclude(
    booking__in=bookings
)

Это приводит к следующему запросу:

SELECT item.*
FROM item
WHERE NOT (
    item.id IN (
        SELECT V1.item_id
        FROM booking V1
        WHERE (V1.id IN (
            SELECT U0.id FROM booking U0
            WHERE (U0.end_date >= 2019-01-01 AND U0.start_date <= 2019-02-02)
            AND V1.item_id IS NOT NULL
        )
    )
)

(здесь 2019-01-01 и 2019-02-02 - гипотетические даты начала и окончания).

Я думаю, что, вероятно, лучше обработать две даты через Form, однако, чтобы сделать правильные проверки и очистки.

Например, если заполнить пустую базу данных данными, указанными в вопросе, мы получим:

>>> i1 = Item.objects.create(name='Item #01')
>>> i2 = Item.objects.create(name='Item #02')
>>> b1 = Booking.objects.create(item=i1, start_date=)
KeyboardInterrupt
>>> from datetime import date
>>> b1 = Booking.objects.create(item=i1, start_date=date(2019,1,1), end_date=date(2019, 1, 3))
>>> b2 = Booking.objects.create(item=i1, start_date=date(2019,1,11), end_date=date(2019, 1, 18))
>>> bookings = Booking.objects.filter(
...     end_date__gte=date(2019, 1, 6),
...     start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
...     booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>, <Item: Item object (3)>]>
>>> b3 = Booking.objects.create(item=i2, start_date=date(2019,1,2), end_date=date(2019, 1, 6))
>>> bookings = Booking.objects.filter(
...     end_date__gte=date(2019, 1, 6),
...     start_date__lte=date(2019, 1, 8)
... )
>>> Item.objects.exclude(
...     booking__in=bookings
... )
<QuerySet [<Item: Item object (2)>]>

Итак, сначала я сконструировал два предмета и сделал два заказа на первый предмет. Если мы затем сделаем запрос, мы увидим, что оба элемента всплывают. Если затем мы добавим дополнительное бронирование для Item #02, то, если мы снова выполним запрос, мы увидим, что отображается только первый элемент (я сначала создал элемент для целей тестирования, который затем был удален), поскольку для данного диапазона , второй элемент больше не доступен: он был забронирован по бронированию b3.

...