【问题标题】:Django filtering a timedelta error: TypeError: expected string or bytes-like objectDjango 过滤 timedelta 错误:TypeError: expected string or bytes-like object
【发布时间】:2020-04-14 20:20:54
【问题描述】:

我有一个模型 Run,其中包含 start_timeend_time 时间戳,表示数据测量的开始和结束(我们称之为“跑”)。

class Run(models.Model):
    start_time = models.DateTimeField(db_index=True)
    end_time = models.DateTimeField()

最近,一位客户开始执行新类型的操作,其中一项要求他们知道运行的持续时间(很容易弄清楚),还需要根据该持续时间过滤表格。

过滤是我没有的部分。因此,尝试按持续时间(模型上没有的字段)进行过滤,我想出了以下查询:

from django.db.models import F

test_query = Run.objects.all().annotate(duration=F('end_time') - F('start_time'))

通过使用 annotate 和 F 操作,我可以在我的查询中添加一个新的临时字段,称为“duration”。新字段的类型为datetime.timedelta,因为它是对两个datetime 对象执行算术运算的结果。该部分按预期工作。

问题归结为尝试通过使用__gte__lte 查找来过滤新生成的注释:

from datetime import timedelta
from django.db.models import F

test_query = Run.objects.all().annotate(duration=F('end_time') - F('start_time')).filter(duration__gte=timedelta(seconds=50))

为简单起见,假设我只想获得 50 秒或更长时间的 Runs,例如。

到目前为止,我在 timedelta 方面的工作非常简单。 end_time - start_time = timedelta_datetime_span

除了django过滤器,它似乎不想把它作为参数,抛出以下错误:

Traceback (most recent call last):
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/lib/python3.5/contextlib.py", line 30, in inner
    return func(*args, **kwds)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/home/foo/Projects/project_foo/project_foo/comp_foo/foo/views.py", line 2790, in RunsJson
    test = Run.objects.all().annotate(duration=F('end_time') - F('start_time')).filter(duration__gte=timedelta(seconds=50))
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/query.py", line 892, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/query.py", line 910, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1290, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1318, in _add_q
    split_subq=split_subq, simple_col=simple_col,
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1207, in build_filter
    condition = self.build_lookup(lookups, reffed_expression, value)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/sql/query.py", line 1116, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/lookups.py", line 20, in __init__
    self.rhs = self.get_prep_lookup()
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/lookups.py", line 70, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 1410, in get_prep_value
    value = super().get_prep_value(value)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 1270, in get_prep_value
    return self.to_python(value)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 1371, in to_python
    parsed = parse_datetime(value)
  File "/home/foo/.virtualenvs/project_foo/lib/python3.5/site-packages/django/utils/dateparse.py", line 106, in parse_datetime
    match = datetime_re.match(value)
TypeError: expected string or bytes-like object

.annotate() 之后的 .filter() 串联应该适用于新的 duration 字段,因为(afaik)__gte__lte 查找适用于 datetime.timedelta 对象。

我在这里过滤有什么问题?

【问题讨论】:

    标签: django python-datetime timedelta django-filter django-annotate


    【解决方案1】:

    在我的一项任务中,我遇到了与您类似的问题。我希望这对你有用。您应该像这样使用ExpressionWrapper 表达式:

    from django.db.models import ExpressionWrapper, DurationField
    
    Run.objects.all().annotate(duration=ExpressionWrapper(F('end_time') - F('start_time'), output_field=DurationField()))
    

    使用此解决方案,您可以使用duration__gteduration__lte

    【讨论】:

    • 效果很好。谢谢你的帮助。我什至不知道 ExpressionWrapper 和那个方便的 DurationField。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-11
    • 2018-05-29
    • 2016-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多