【发布时间】: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