Django Admin 中的外键日期范围过滤

foreign key daterange filtering in django admin

提问人:Tachyon6 提问时间:11/4/2023 更新时间:11/19/2023 访问量:122

问:

对于使用 django admin 的酒店预订系统,我想过滤给定日期范围内的可用房间,模型如下


class Room(models.Model):
    id = models.AutoField(primary_key=True)
    hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE)


class Booking(models.Model):
    id = models.AutoField(primary_key=True)
    start_date = models.DateField(verbose_name=_("Start date"))
    end_date = models.DateField(verbose_name=_("End date"))
    room = models.ForeignKey(Room, on_delete=models.CASCADE)

如何在后台中筛选房间,以便选择日期范围并仅返回所选范围内没有预订的房间?

多谢

django django-admin

评论

0赞 inquirer 11/15/2023
在下面发布了答案。

答:

1赞 inquirer 11/6/2023 #1

为了传递范围数据,我在管理员中创建了一个表单。为此,我覆盖了 .该模板将添加到以下目录中:。admin/change_list.html templatechange_list.htmltemplates/admin

我尝试仅使用 Django 进行过滤,例如:

response.context_data['cl'].result_list = (
                        Room.objects.filter(~Q(booking__end_date__range=val) &
                                            ~Q(booking__start_date__range=val)))

但结果并不完全正确。

我认为需要从每个预订记录创建一个日期范围,并将其与从表单日期创建的范围进行比较。Pandas 非常适合此目的。 使用以下方法:date_rangeisinloc

admin.py

import pandas as pd
from django.contrib import admin
from .forms import TestForm
from .models import *

class Room_Admin(admin.ModelAdmin):
    list_display = ('id', 'hotel')
    change_list_template = 'admin/change_list.html'

    def changelist_view(self, request, extra_context=None):
        response = super().changelist_view(
            request,
            extra_context=extra_context,
        )

        if request.method != 'POST':
            response.context_data['form'] = TestForm()
        else:
            form = TestForm(request.POST)
            if form.is_valid():
                response.context_data['form'] = form
                # Getting the start and end from the form
                val = [form.cleaned_data.get('start'), form.cleaned_data.get('end')]

                bkg = Booking.objects.values('start_date', 'end_date', 'room')
                # Create a list based on form dates from and to
                custom_range = pd.date_range(start=val[0], end=val[1])

                df = pd.DataFrame(bkg)

                def f(x):
                    abc = pd.date_range(
                        start=df.loc[x, 'start_date'], end=df.loc[x, 'end_date']
                    ).isin(custom_range)

                    return ~abc.any()

                # List of rooms based on the secondary Booking model whose
                # dates do not intersect with custom_range dates
                room = [df.loc[i, 'room'] for i in range(len(df)) if f(i)]

                # room numbers that are not booked at all
                no_booking = [i.id for i in Room.objects.all() if len(i.booking_set.all()) < 1]
                room = room + no_booking
                response.context_data['cl'].result_list = Room.objects.filter(id__in=room)


                return super(Room_Admin, self).changelist_view(request, extra_context=response.context_data)

        return response


admin.site.register(Room, Room_Admin)

forms.py

class TestForm(forms.Form):
    start = forms.DateField(widget=forms.SelectDateWidget(years=range(2022, 2025)))
    end = forms.DateField(widget=forms.SelectDateWidget(years=range(2022, 2025)))

模板

{% extends 'admin/change_list.html' %}

{% block result_list %}
<form action='' method='POST'>
    {% csrf_token %}
    {{ form.as_p }}
    <input type='submit' value='adding'/>
</form>

{{block.super}}

{% endblock %}