【问题标题】:DeserializationError: ['“F(view_count) + Value(1)” value must be an integer.']DeserializationError: ['“F(view_count) + Value(1)” value must be an integer.']
【发布时间】:2021-08-31 10:43:04
【问题描述】:

我正在使用 Django 3.2

我正在使用 F 值来增加字段值(以避免潜在的竞争条件)-as recommended in the offical docs

这是我的代码的 sn-p(引发异常的部分):

  def __update_fields(self, interaction_type):

        if interaction_type == Engagement.VIEW:
            self.view_count = F('view_count') + 1
        #elif check which field needs updating ...

        self.save() # By the time we get here, the necessary field (NOTE: **SINGULAR** word 'field') would have been updated

        self.refresh_from_db()   

我收到此错误:

DeserializationError: ['“F(view_count) + Value(1)” value must be an integer.']: (poll.poll:pk=10) field_value was 'F(view_count) + Value(1)'

这是完整的堆栈跟踪:

Internal Server Error: //path/to/example
Traceback (most recent call last):
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 1836, in to_python
    return int(value)
ValueError: invalid literal for int() with base 10: 'F(view_count) + Value(1)'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/core/serializers/python.py", line 144, in Deserializer
    data[field.name] = field.to_python(field_value)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 1838, in to_python
    raise exceptions.ValidationError(
django.core.exceptions.ValidationError: ['“F(view_count) + Value(1)” value must be an integer.']

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/views/generic/detail.py", line 107, in get
    context = self.get_context_data(object=self.object)
  File "/path/to/myproj/poll/views.py", line 513, in get_context_data
    poll.interact(self.request.user, Engagement.VIEW, self.request)
  File "/path/to/myproj/social/models.py", line 988, in interact
    self.__update_fields(interaction_type)
  File "/path/to/myproj/social/models.py", line 947, in __update_fields
    self.save()
  File "/path/to/myproj/poll/models.py", line 97, in save
    super().save(*args, **kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/base.py", line 726, in save
    self.save_base(using=using, force_insert=force_insert,
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/base.py", line 774, in save_base
    post_save.send(
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 180, in send
    return [
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/path/to/myproj/moderation/register.py", line 321, in post_save_handler
    moderated_obj = ModeratedObject.objects.get_for_instance(instance)
  File "/path/to/myproj/moderation/managers.py", line 73, in get_for_instance
    moderated_object = self.get(object_pk=instance.pk,
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/query.py", line 431, in get
    num = len(clone)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/query.py", line 262, in __len__
    self._fetch_all()
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/query.py", line 1324, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/query.py", line 69, in __iter__
    obj = model_cls.from_db(db, init_list, row[model_fields_start:model_fields_end])
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/base.py", line 515, in from_db
    new = cls(*values)
  File "/path/to/myproj/moderation/models.py", line 66, in __init__
    super().__init__(*args, **kwargs)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/db/models/base.py", line 505, in __init__
    post_init.send(sender=cls, instance=self)
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 180, in send
    return [
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 181, in <listcomp>
    (receiver, receiver(signal=self, sender=sender, **named))
  File "/path/to/myproj/moderation/fields.py", line 90, in post_init
    self._deserialize(value))
  File "/path/to/myproj/moderation/fields.py", line 54, in _deserialize
    obj = next(obj_generator).object
  File "/path/to/myproj/env/lib/python3.8/site-packages/djmoney/serializers.py", line 55, in Deserializer
    for inner_obj in PythonDeserializer([obj], **options):
  File "/path/to/myproj/env/lib/python3.8/site-packages/django/core/serializers/python.py", line 146, in Deserializer
    raise base.DeserializationError.WithData(e, d['model'], d.get('pk'), field_value)
django.core.serializers.base.DeserializationError: ['“F(view_count) + Value(1)” value must be an integer.']: (poll.poll:pk=10) field_value was 'F(view_count) + Value(1)'

这个神秘错误的含义是什么(我传递的值[1],显然是一个整数)。我该如何解决这个问题?

[[编辑]]

我在调试控制台中做了一些额外的调查,看看表达式 F('field_name') + 1 实际返回了什么

结果如下:

my_object.view_count   # This is immediately after the assignment `F() + var`
<CombinedExpression: F(view_count) + Value(1)>
my_object.save()       # Saving to see if exception thown (none thrown)
None
photo.view_count       # Checking to see what on earth is stored in the field
<CombinedExpression

所以,似乎 django 文档是错误,或者我误解了如何安全地增加字段值 - 直接在数据库中编写 SP 可能会更快,甚至编写一个线程安全的类来处理这个问题——这真的不应该这么难。

【问题讨论】:

  • here 的文档中进行了解释,@4140tm 的回答也分享了这一点

标签: django django-models django-queryset race-condition


【解决方案1】:

将我的评论转换为答案以添加更多上下文。

是否可能 view_count 的初始值不是整数?例如没有?

另外,从堆栈跟踪中我看到在__update_fields 之后执行了更多自定义方法。如果在当前实例的view_count 上执行更多操作,则可能因为this 而出现问题。基本上,如果以后使用相同的python实例,view_count的值将是&lt;CombinedExpression: F(view_count) + Value(1)&gt;

您可能需要在保存后从数据库中刷新实例。

【讨论】:

  • 嗯,这很令人沮丧——尤其是当我正在做official docs 中建议的所有事情时。为了解决您提出的观点:1. 数据库字段的初始值为(整数)零。 2. 我使用 if 语句,因为 Python 没有选择;逻辑仅流经一个段,(在该分支之后没有进一步处理 - 除了最终的 save() 方法。感谢您的尝试。
  • 这个答案和共享的链接似乎可以解释这个问题。在接收器中,再次从数据库中获取实例应该可以解决此问题
  • "基本上,view_count 的值将是 " 这确实是问题的症结。但是,如何将其保存到数据库中?更新实例并不能保存到数据库是不好的。当我尝试保存到数据库时,即引发错误,因为显然 Db 期望的是整数而不是 CombinedExpression 值。 ://
  • 保存到 db 时不会出现问题(由您的 shell sn-p 证明)。随着更多操作的执行,它会在稍后发生。如文档中所述,要从 db 取回实际字段值,您可以执行 &lt;instance&gt;.refresh_from_db()。所以,在__update_fields 中的self.save() 之后做self.refresh_from_db()
  • 当前模型上的 save 方法似乎调用了另一个 ``` File "/path/to/myproj/social/models.py",第 947 行,在 __update_fields self. save() 文件“/path/to/myproj/poll/models.py”,第 97 行,在 save super().save(*args, **kwargs) ``` 这是父级吗?那里会发生什么?如果您无法访问self.refresh_from_db(),您可能需要进行一些重组(我看到您已经更新了示例)。如果您从 shell sn-p 重复这些步骤;执行my_object.refresh_from_db()my_object.view_count,你会看到正确的值
【解决方案2】:

试试Value(F('view_count') + 1, output_field=IntegerField())。这可能有效。

【讨论】:

  • 我现在收到错误:"ProgrammingError("can't adapt type 'CombinedExpression
  • 你能添加整个查询吗
猜你喜欢
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 2013-09-24
  • 2014-11-16
  • 1970-01-01
  • 2018-03-31
  • 1970-01-01
相关资源
最近更新 更多