如何在mongoengine Document中设置字段只读并允许条件更新?
问题
我有一个如下的MongoEngine文档:
from mongoengine.document import Document from mongoengine import StringField class Class_A(Document): field_a = StringField() field_b = StringField()
我希望锁定field_b使其无法被修改,例如执行以下代码时:
var = <从数据库获取Class_a文档> var.field_b = 'abc'
会触发错误。
但我需要在设置field_a时能够自动设置field_b。例如,field_a是数据内容,field_b是该数据的计算哈希值——用户可设置数据,但不能手动修改哈希值,哈希值应仅在数据被赋值时自动设置。
我尝试使用__setattr__/__dict__方法,但MongoEngine背后似乎有属性处理的魔法逻辑,导致该方案无法生效。我也曾考虑继承StringField并使用元类包装其__setattr__方法,但效果同样不理想。
请问如何实现这种需求?
解决方案
要实现这个需求,需要结合自定义只读字段和文档生命周期钩子来实现:
1. 自定义只读字段,阻止手动修改
MongoEngine的字段基于描述符实现,直接重写文档的__setattr__无法拦截字段赋值操作,因此需要自定义字段并重写其__set__方法,仅允许特定场景修改field_b:
from mongoengine import StringField class ReadOnlyStringField(StringField): def __set__(self, instance, value): # 已保存的文档,仅允许通过内部标记修改该字段 if instance.pk is not None and not getattr(instance, '_allow_ro_set', False): raise AttributeError(f"字段 {self.name} 不允许手动修改") super().__set__(instance, value)
2. 关联field_a与field_b的自动更新
利用MongoEngine的clean方法(保存文档前自动触发),在field_a变更时自动计算并设置field_b,同时通过临时标记绕过只读限制:
from mongoengine.document import Document import hashlib class Class_A(Document): field_a = StringField() field_b = ReadOnlyStringField() def clean(self): # 仅当field_a有值时计算哈希 if self.field_a is not None: # 设置临时标记允许修改field_b self._allow_ro_set = True # 计算field_a的SHA256哈希作为field_b的值 self.field_b = hashlib.sha256(self.field_a.encode('utf-8')).hexdigest() # 删除临时标记 delattr(self, '_allow_ro_set') super().clean()
3. 验证效果
- 创建新文档时,
clean方法会自动计算field_b:doc = Class_A(field_a="test content").save() print(doc.field_b) # 输出对应的哈希值 - 尝试手动修改已保存文档的
field_b会触发错误:doc = Class_A.objects.first() doc.field_b = "new value" # 抛出AttributeError异常 - 修改
field_a并保存时,clean方法会自动更新field_b:doc = Class_A.objects.first() doc.field_a = "updated content" doc.save() print(doc.field_b) # 输出更新后内容的哈希值
补充说明
如果希望在修改field_a后立即更新field_b(无需等到保存),可以重写field_a的字段__set__方法:
class TriggerStringField(StringField): def __set__(self, instance, value): super().__set__(instance, value) # 当field_a被设置时,直接触发field_b的更新 if instance.pk is not None and value is not None: instance._allow_ro_set = True instance.field_b = hashlib.sha256(value.encode('utf-8')).hexdigest() delattr(instance, '_allow_ro_set') class Class_A(Document): field_a = TriggerStringField() field_b = ReadOnlyStringField()
这样修改field_a后,field_b会立刻更新,无需调用save(),同时依然阻止手动修改field_b。
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

