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

Django ORM按天数字段+datetime字段过滤过期记录并适配Admin筛选

Django Admin过期筛选项与ORM过滤实现方案

你的原有@property属性仅支持单实例调用,无法直接用于ORM过滤和Admin筛选,因为该属性是Python实例层逻辑,数据库层面没有对应字段,这也是你之前自定义Filter报错的核心原因。可按以下方案实现需求:


步骤1:扩展模型QuerySet,实现ORM层面直接过滤

首先给模型自定义查询集方法,把过期判断逻辑转换为数据库可识别的表达式,既支持批量过滤,也可以给Admin筛选项复用:

from django.db import models, timezone
from django.db.models import F, ExpressionWrapper, DateTimeField, Q
from datetime import timedelta

# 自定义查询集
class AccessDurationQuerySet(models.QuerySet):
    def with_expiration(self):
        """给查询集预注解到期时间字段"""
        return self.annotate(
            db_expiration_date=ExpressionWrapper(
                F('lab_start_date') + ExpressionWrapper(
                    timedelta(days=1) * F('duration'),
                    output_field=DateTimeField()
                ),
                output_field=DateTimeField()
            )
        )

    def filter_by_expired_status(self, is_expired: bool):
        """按是否过期过滤数据集
        这里默认lab_start_date为空时视为未过期,可根据业务调整判断逻辑
        """
        qs = self.with_expiration()
        if is_expired:
            return qs.filter(
                Q(lab_start_date__isnull=False)
                & Q(db_expiration_date__lt=timezone.now())
            )
        else:
            return qs.filter(
                Q(lab_start_date__isnull=True)
                | Q(db_expiration_date__gte=timezone.now())
            )

# 你的原有模型
class AccessDuration(models.Model):
    duration = models.PositiveIntegerField(default=30, verbose_name="有效天数")
    lab_start_date = models.DateTimeField(null=True, blank=True, verbose_name="启用时间")

    # 绑定自定义查询集到模型Manager
    objects = AccessDurationQuerySet.as_manager()

    # 保留原有@property给单实例使用
    @property
    def expiration_date(self):
        if not self.lab_start_date:
            return None
        return self.lab_start_date + timedelta(days=self.duration)
    
    @property
    def is_expired(self):
        exp_date = self.expiration_date
        return exp_date < timezone.now() if exp_date else False

配置完成后你就可以直接用ORM语句按过期状态过滤数据集:

  • 查询所有已过期记录:AccessDuration.objects.filter_by_expired_status(is_expired=True)
  • 查询所有未过期记录:AccessDuration.objects.filter_by_expired_status(is_expired=False)

步骤2:实现Django Admin列表页筛选项

在admin.py中自定义SimpleListFilter,直接复用上面已经写好的查询集方法即可:

from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.utils.translation import gettext_lazy as _
from .models import AccessDuration

class ExpiredStatusFilter(SimpleListFilter):
    title = _("是否过期")
    parameter_name = "expired_status"

    def lookups(self, request, model_admin):
        # 筛选项的下拉选项
        return (
            ("1", _("已过期")),
            ("0", _("未过期")),
        )

    def queryset(self, request, queryset):
        # 按选择的筛选项过滤查询集
        if self.value() == "1":
            return queryset.filter_by_expired_status(is_expired=True)
        elif self.value() == "0":
            return queryset.filter_by_expired_status(is_expired=False)
        return queryset

@admin.register(AccessDuration)
class AccessDurationAdmin(admin.ModelAdmin):
    # 把自定义筛选项加入列表页过滤栏
    list_filter = (ExpiredStatusFilter,)
    # 其他原有Admin配置保持不变

注意事项

  • 时间运算必须使用Django ORM表达式(F、ExpressionWrapper),不能直接用Python的datetime计算,否则逻辑会在Python端执行,批量查询时结果错误
  • 若业务中lab_start_date为空的判断逻辑不同,只需修改filter_by_expired_status方法中的过滤条件即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 00:18:03