You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Admin中外键日期范围筛选:酒店空房查询

在Django Admin中实现指定日期范围筛选可用房间

需求背景

需要在基于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)

实现方案

1. 自定义日期范围筛选器

Django Admin默认筛选器无法直接实现"排除有冲突预订"的逻辑,需自定义SimpleListFilter处理查询:

# admin.py
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from .models import Room, Booking

class AvailableRoomFilter(admin.SimpleListFilter):
    title = _('可用日期范围')
    parameter_name = 'available_range'

    def lookups(self, request, model_admin):
        # 无需预设选项,通过后续自定义表单接收用户输入
        return ()

    def queryset(self, request, queryset):
        # 获取用户提交的日期参数
        start_date = request.GET.get('available_start')
        end_date = request.GET.get('available_end')

        if start_date and end_date:
            # 核心逻辑:排除所有与目标时间段有冲突的房间
            # 冲突判定:预订开始日期 < 筛选结束日期 且 预订结束日期 > 筛选开始日期
            return queryset.exclude(
                booking__start_date__lt=end_date,
                booking__end_date__gt=start_date
            )
        # 日期参数不全时,返回所有房间
        return queryset

2. 配置RoomAdmin并传递参数

在RoomAdmin中注册自定义筛选器,修改changelist_view将日期参数传递到模板:

@admin.register(Room)
class RoomAdmin(admin.ModelAdmin):
    list_filter = (AvailableRoomFilter,)

    def changelist_view(self, request, extra_context=None):
        # 保留用户输入的日期状态,传递到模板
        extra_context = extra_context or {}
        extra_context['available_start'] = request.GET.get('available_start', '')
        extra_context['available_end'] = request.GET.get('available_end', '')
        return super().changelist_view(request, extra_context)

3. 添加自定义Admin模板

创建模板文件templates/admin/room/change_list.html(路径对应你的app名称),实现日期输入表单:

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

{% block filters %}
    {{ block.super }}
    <div class="module filtered" id="changelist-filter">
        <h2>可用日期筛选</h2>
        <form method="get">
            <!-- 保留其他已选择的筛选参数,避免丢失状态 -->
            {% for key, value in request.GET.items %}
                {% if key not in 'available_start,available_end' %}
                    <input type="hidden" name="{{ key }}" value="{{ value }}">
                {% endif %}
            {% endfor %}
            <p>
                <label for="available_start">开始日期:</label>
                <input type="date" name="available_start" id="available_start" value="{{ available_start }}">
            </p>
            <p>
                <label for="available_end">结束日期:</label>
                <input type="date" name="available_end" id="available_end" value="{{ available_end }}">
            </p>
            <button type="submit" class="button">筛选</button>
        </form>
    </div>
{% endblock %}

关键说明

  • 冲突判定逻辑:通过booking__start_date__lt=end_date和booking__end_date__gt=start_date,可以准确覆盖所有与目标时间段重叠的预订(包括部分重叠场景)。
  • 参数保留:表单通过隐藏字段保留其他筛选参数,切换筛选条件时不会丢失已选状态。
  • 日期兼容性:HTML5的date输入控件返回的格式与DjangoDateField完全兼容,无需额外格式转换。

内容的提问来源于stack exchange,提问作者Tachyon6

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 22:13:22