Django - F('datetimefield') + relativedelta(months=1) 失败

Django - F('datetimefield') + relativedelta(months=1) fails

我正在尝试使用 Django 中的 F() 对象更新模型的自日期时间字段。

HeatWatchList.objects.filter(
    user=request.user,
    next_date_to__lt=datetime.combine(datetime.now().date(), time.min)
).update(
    next_date_from = F('next_date_from') + relativedelta(months=1),
    next_date_to = F('next_date_to') + relativedelta(months=1)
)

但是当这样做时我得到了 AttributeError: relativedelta object has no attribute translate

这是我在执行代码时得到的回溯,也许在使用日期时间类型 relativedelta 添加值时使用 F() 对象时出现问题。

Traceback (most recent call last):
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\core\handlers\exception.py", line 39, in inner
    response = get_response(request)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\views\decorators\csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\rest_framework\viewsets.py", line 83, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\rest_framework\views.py", line 477, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\rest_framework\views.py", line 437, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\rest_framework\views.py", line 474, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\farm_management\heat\views.py", line 192, in update_watchlist
    next_date_to = F('next_date_to') + relativedelta(months=1)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\models\query.py", line 637, in update
    rows = query.get_compiler(self.db).execute_sql(CURSOR)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\models\sql\compiler.py", line 1148, in execute_sql
    cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\models\sql\compiler.py", line 835, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\backends\utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\backends\utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\django\db\backends\mysql\base.py", line 110, in execute
    return self.cursor.execute(query, args)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\cursors.py", line 164, in execute
    query = self.mogrify(query, args)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\cursors.py", line 143, in mogrify
    query = query % self._escape_args(args, conn)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\cursors.py", line 118, in _escape_args
    return tuple(conn.literal(arg) for arg in args)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\cursors.py", line 118, in <genexpr>
    return tuple(conn.literal(arg) for arg in args)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\connections.py", line 800, in literal
    return self.escape(obj, self.encoders)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\connections.py", line 793, in escape
    return escape_item(obj, self.charset, mapping=mapping)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\converters.py", line 27, in escape_item
    val = encoder(val, mapping)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\converters.py", line 110, in escape_unicode
    return u"'%s'" % _escape_unicode(value)
  File "C:\Users\Web\Desktop\PyDev\Envs\djangular\lib\site-packages\pymysql\converters.py", line 73, in _escape_unicode
    return value.translate(_escape_table)
AttributeError: 'relativedelta' object has no attribute 'translate'

似乎是 pymysql 的转换器 does not support relativedelta 对象。 一种可能的解决方案是遍历对象并更新它(效率低下):

objects_list = HeatWatchList.objects.filter(
user=request.user,
next_date_to__lt=datetime.combine(datetime.now().date(), time.min)
)

for obj in object_list:
    obj.next_date_from = onj.next_date_from + relativedelta(months=1)
    obj.next_date_to = onj.next_date_to + relativedelta(months=1)
    objsave()  

或者如果可能,最好使用 datetime.timedelta 而不是 relativedelta

HeatWatchList.objects.filter(
user=request.user,
next_date_to__lt=datetime.combine(datetime.now().date(), time.min)
).update(
next_date_from = F('next_date_from') + timedelta(days=31),
next_date_to = F('next_date_to') + timedelta(days=31)
)