过滤最接近给定日期时间的日期时间

时间:2013-04-06 20:32:19

标签: django

我有一个具有日期时间字段的模型。现在给定一个特定的日期时间 - DT,我需要得到日期时间最接近DT的对象。这可能吗?

谢谢,

4 个答案:

答案 0 :(得分:16)

您可以通过两个查询和一些逻辑来获取它:

我们的想法是在目标日期时间之后立即找到一个对象并返回其中一个,并返回其中一个:

# this method is on the model's manager
def get_closest_to(self, target)
    closest_greater_qs = self.filter(dt__gt=target).order_by('dt')
    closest_less_qs    = self.filter(dt__lt=target).order_by('-dt')

    try:
        try:
            closest_greater = closest_greater_qs[0]
        except IndexError:
            return closest_less_qs[0]

        try:
            closest_less = closest_less_qs[0]
        except IndexError:
            return closest_greater_qs[0]
    except IndexError:
        raise self.model.DoesNotExist("There is no closest object"
                                      " because there are no objects.")

    if closest_greater.dt - target > target - closest_less.dt:
        return closest_less
    else:
        return closest_greater

要使用一个查询来获取它,您必须将ORM退出到原始SQL。

答案 1 :(得分:2)

我想完成Pavel的回答,如:

dt__gte dt__lte

应该使用

提供确切日期时过滤将无法正常工作。

答案 2 :(得分:1)

上述版本的通用实现,可用于任意模型。

def get_closest(
    *,
    qs: models.QuerySet,
    datetime_field: str,
    target_datetime: Union[datetime.datetime, datetime.date],
) -> models.Model:

    greater = qs.filter(**{
        f'{datetime_field}__gte': target_datetime,
    }).order_by(datetime_field).first()

    less = qs.filter(**{
        f'{datetime_field}__lte': target_datetime,
    }).order_by(f'-{datetime_field}').first()

    if greater and less:
        greater_datetime = getattr(greater, datetime_field)
        less_datetime = getattr(less, datetime_field)
        return greater if abs(greater_datetime - target_datetime) < abs(less_datetime - target_datetime) else less
    else:
        return greater or less

答案 3 :(得分:0)

只需将Pavlel的答案更改为:

def get_closest_to_dt(qs, dt):
    greater = qs.filter(dt__gte=dt).order_by("dt").first()
    less = qs.filter(dt__lte=dt).order_by("-dt").first()

    if greater and less:
        return greater if abs(greater.dt - dt) < abs(less.dt - dt) else less
    else:
        return greater or less