如何在覆盖的 Document.save 方法中处理可能的竞争条件 - Mongoengine
How to handle possible race-condition in overwritten Document.save method - Mongoengine
我正在使用 flask-mongoengine 并认为我 运行 在某种竞争条件下试图覆盖 Document.save 方法。
我的模型(简化版)如下所示:
class User(Document):
meta = {"collection": "users"}
name = StringField()
class Group(Document):
meta = {"collection": "groups"}
name = StringField()
class History(EmbeddedDocument):
key = StringField()
oldValue = StringField()
newValue = StringField()
class Asset(DynamicDocument):
meta = {"collection": "assets"}
c_id = SequenceField()
name = StringField()
history = ListField(EmbeddedDocumentField(History))
user = ReferenceField('User')
group = ReferenceField('Group', required=True, default=Group.objects.first())
def save(self, **kwargs):
for key, value in self._data.items():
history_update = History(
key=key,
oldValue="",
newValue=str(value)
)
self.history.append(history_update)
return super(Asset, self).save(**kwargs)
我想要实现的是:
创建 Asset
类型的新文档时,为每个 Key/Value 对更改的文档添加一个历史记录类型的条目。 (这里从 None
到一些 value
,我在现有资产的更新方法中有类似的代码)。此历史列表应该类似于特定资产在其生命周期内的变更日志。
我当前实施的问题是:
c_id
类型 SequenceField
在我的 for 循环中是 None
。
str(value)
对于 User
对象给了我正确的用户对象(或者我的自定义 __str__
方法的结果)但是 str(value)
对于 Group
对象给我 DBRef('groups', '<mongoidstring>')
并且不会触发我的客户 str 方法
- 事先使用断点进行调试时,不会出现这两个错误。
c_id
具有正确的值,我的 group
对象是一个组对象而不是 DBRef
对象
我之前尝试过保存文档一次,然后添加我的历史记录,这至少给了我一个正确的 c_id
但该组仍然是 DBRef
.
我确实认为 SequenceField
是并行填充的,因此当我尝试访问它时仍然是 None
,但当我通过调试器时却不是。但是 DBRef
仍然让我头疼。而且我真的没有看到通过覆盖保存方法来正确实现我的 ChangeHistory 的方法。任何想法如何正确处理这个?
所以我自己找到了答案(有点)。
- SequenceFields 仅在 save() 期间填充。当覆盖保存方法时,我们首先必须创建一个 super.save 来获取 SequenceField 值,或者我们必须通过 mongoengine 创建的助手集合来假定它的值。我采取了简单的方法,只是添加了一个
super(Asset, self).save()
并且 c_id 是为之后的更改而设置的。
- ReferenceFields 在您第一次从 Document 对象访问它之前可以用作 DBRef。
只需预先添加某种检查以确保正确解析其值,例如:
assert self.group is not None
assert self.user is not None
我正在使用 flask-mongoengine 并认为我 运行 在某种竞争条件下试图覆盖 Document.save 方法。
我的模型(简化版)如下所示:
class User(Document):
meta = {"collection": "users"}
name = StringField()
class Group(Document):
meta = {"collection": "groups"}
name = StringField()
class History(EmbeddedDocument):
key = StringField()
oldValue = StringField()
newValue = StringField()
class Asset(DynamicDocument):
meta = {"collection": "assets"}
c_id = SequenceField()
name = StringField()
history = ListField(EmbeddedDocumentField(History))
user = ReferenceField('User')
group = ReferenceField('Group', required=True, default=Group.objects.first())
def save(self, **kwargs):
for key, value in self._data.items():
history_update = History(
key=key,
oldValue="",
newValue=str(value)
)
self.history.append(history_update)
return super(Asset, self).save(**kwargs)
我想要实现的是:
创建 Asset
类型的新文档时,为每个 Key/Value 对更改的文档添加一个历史记录类型的条目。 (这里从 None
到一些 value
,我在现有资产的更新方法中有类似的代码)。此历史列表应该类似于特定资产在其生命周期内的变更日志。
我当前实施的问题是:
c_id
类型SequenceField
在我的 for 循环中是None
。str(value)
对于User
对象给了我正确的用户对象(或者我的自定义__str__
方法的结果)但是str(value)
对于Group
对象给我DBRef('groups', '<mongoidstring>')
并且不会触发我的客户 str 方法- 事先使用断点进行调试时,不会出现这两个错误。
c_id
具有正确的值,我的group
对象是一个组对象而不是DBRef
对象
我之前尝试过保存文档一次,然后添加我的历史记录,这至少给了我一个正确的 c_id
但该组仍然是 DBRef
.
我确实认为 SequenceField
是并行填充的,因此当我尝试访问它时仍然是 None
,但当我通过调试器时却不是。但是 DBRef
仍然让我头疼。而且我真的没有看到通过覆盖保存方法来正确实现我的 ChangeHistory 的方法。任何想法如何正确处理这个?
所以我自己找到了答案(有点)。
- SequenceFields 仅在 save() 期间填充。当覆盖保存方法时,我们首先必须创建一个 super.save 来获取 SequenceField 值,或者我们必须通过 mongoengine 创建的助手集合来假定它的值。我采取了简单的方法,只是添加了一个
super(Asset, self).save()
并且 c_id 是为之后的更改而设置的。 - ReferenceFields 在您第一次从 Document 对象访问它之前可以用作 DBRef。 只需预先添加某种检查以确保正确解析其值,例如:
assert self.group is not None
assert self.user is not None