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

如何在Django 1.11.4 Admin中添加双过滤器并对比两组查询结果?

在Django 1.11.4 Admin后台添加双日期范围过滤器实现结果对比

要实现同时添加两个日期范围过滤器并直观对比两组结果,我们可以通过自定义Admin列表过滤器+自定义列表字段标记分组来完成,具体步骤和代码如下:

1. 自定义两个独立的日期范围过滤器

Django自带的DateFieldListFilter只能处理单个日期范围,所以我们需要继承它创建两个过滤器,分别对应“日期范围1”和“日期范围2”,各自处理独立的筛选参数。

2. 在Admin类中注册过滤器

将自定义过滤器绑定到目标日期字段,这样Admin侧边栏会显示两个独立的日期范围筛选控件。

3. 添加自定义列表字段标记记录所属范围

为了直观区分两组结果,我们可以在列表中新增一个字段,判断每条记录属于哪个日期范围(范围1、范围2、都包含、都不包含),并用不同颜色标记。

完整代码示例

假设你的模型是Order,日期字段为create_time,代码如下:

from django.contrib import admin
from django.contrib.admin.filters import DateFieldListFilter
from django.utils.translation import ugettext_lazy as _
from django.utils.html import format_html
from .models import Order

# 自定义第一个日期范围过滤器
class DateRangeFilter1(DateFieldListFilter):
    title = _('日期范围1')
    parameter_name = 'date_range1'

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(field, request, params, model, model_admin, field_path)
        self.start_param = f'{self.parameter_name}_start'
        self.end_param = f'{self.parameter_name}_end'
        self.start_date = params.get(self.start_param)
        self.end_date = params.get(self.end_param)

    def queryset(self, request, queryset):
        if self.start_date and self.end_date:
            return queryset.filter(
                create_time__gte=self.start_date,
                create_time__lte=self.end_date
            )
        return queryset

    def expected_parameters(self):
        return [self.start_param, self.end_param]

    def choices(self, changelist):
        # 显示“所有日期”重置选项
        yield {
            'selected': self.start_date is None and self.end_date is None,
            'query_string': changelist.get_query_string({}, [self.start_param, self.end_param]),
            'display': _('所有日期'),
        }
        # 渲染日期范围输入框
        display_text = _('日期范围1: %(start)s 至 %(end)s') % {'start': self.start_date or '', 'end': self.end_date or ''} if (self.start_date or self.end_date) else _('选择日期范围1')
        yield {
            'selected': False,
            'query_string': '',
            'display': display_text,
            'input_html': f'''
                <input type="text" name="{self.start_param}" placeholder="YYYY-MM-DD" value="{self.start_date or ''}" class="vDateField">
                <span style="margin: 0 5px;">至</span>
                <input type="text" name="{self.end_param}" placeholder="YYYY-MM-DD" value="{self.end_date or ''}" class="vDateField">
                <button type="submit" class="button" style="margin-left: 5px;">筛选</button>
            ''',
        }

# 自定义第二个日期范围过滤器(仅修改标题和参数前缀)
class DateRangeFilter2(DateFieldListFilter):
    title = _('日期范围2')
    parameter_name = 'date_range2'

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(field, request, params, model, model_admin, field_path)
        self.start_param = f'{self.parameter_name}_start'
        self.end_param = f'{self.parameter_name}_end'
        self.start_date = params.get(self.start_param)
        self.end_date = params.get(self.end_param)

    def queryset(self, request, queryset):
        if self.start_date and self.end_date:
            return queryset.filter(
                create_time__gte=self.start_date,
                create_time__lte=self.end_date
            )
        return queryset

    def expected_parameters(self):
        return [self.start_param, self.end_param]

    def choices(self, changelist):
        yield {
            'selected': self.start_date is None and self.end_date is None,
            'query_string': changelist.get_query_string({}, [self.start_param, self.end_param]),
            'display': _('所有日期'),
        }
        display_text = _('日期范围2: %(start)s 至 %(end)s') % {'start': self.start_date or '', 'end': self.end_date or ''} if (self.start_date or self.end_date) else _('选择日期范围2')
        yield {
            'selected': False,
            'query_string': '',
            'display': display_text,
            'input_html': f'''
                <input type="text" name="{self.start_param}" placeholder="YYYY-MM-DD" value="{self.start_date or ''}" class="vDateField">
                <span style="margin: 0 5px;">至</span>
                <input type="text" name="{self.end_param}" placeholder="YYYY-MM-DD" value="{self.end_date or ''}" class="vDateField">
                <button type="submit" class="button" style="margin-left: 5px;">筛选</button>
            ''',
        }

@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
    list_display = ('id', 'order_no', 'create_time', 'date_range_group')
    list_filter = (
        ('create_time', DateRangeFilter1),
        ('create_time', DateRangeFilter2),
    )

    def changelist_view(self, request, extra_context=None):
        # 保存request到实例,供自定义字段方法调用
        self.request = request
        return super().changelist_view(request, extra_context)

    def date_range_group(self, obj):
        # 获取两个范围的筛选参数
        request = self.request
        start1 = request.GET.get('date_range1_start')
        end1 = request.GET.get('date_range1_end')
        start2 = request.GET.get('date_range2_start')
        end2 = request.GET.get('date_range2_end')

        in_range1 = False
        in_range2 = False

        # 判断是否在范围1内(仅比较日期部分,忽略时间)
        if start1 and end1:
            in_range1 = start1 <= str(obj.create_time.date()) <= end1
        # 判断是否在范围2内
        if start2 and end2:
            in_range2 = start2 <= str(obj.create_time.date()) <= end2

        if in_range1 and in_range2:
            return format_html('<span style="color: purple;">两个范围都包含</span>')
        elif in_range1:
            return format_html('<span style="color: blue;">范围1</span>')
        elif in_range2:
            return format_html('<span style="color: green;">范围2</span>')
        else:
            return format_html('<span style="color: gray;">不在任何范围</span>')
    date_range_group.short_description = '所属范围'

使用说明

  1. 打开Admin列表页,侧边栏会出现两个日期范围筛选器,分别输入要对比的日期(比如2024-01-01至2024-02-01和2024-05-01至2024-06-01),点击筛选按钮。
  2. 列表中的“所属范围”字段会用不同颜色标记每条记录的分组,直观区分两组结果。
  3. 如果需要同时查看两组结果,保留两个范围的筛选条件即可,无需重置任何过滤器。

注意事项

  • 日期输入需遵循YYYY-MM-DD格式,与Django默认日期控件一致。
  • 代码中使用format_html确保HTML渲染安全,避免XSS风险。
  • 若你的日期字段包含时间,用obj.create_time.date()仅比较日期部分,避免时间干扰判断。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:47:34