I have a mongoengine
document such as:
from mongoengine.document import Document
from mongoengine import StringField
class Class_A(Document):
field_a = StringField()
field_b = StringField()
I’d like to lock field_b
so it cannot be altered, e.g.
var = <fetch Class_a document from DB>
var.field = 'abc'
would raise an error.
This on itself is not a problem, but I’d like to be able to set field_b
when field_a
is set. To give an example, field_a
could be some data and field_b
would be computed hash for this data – user can set the data, but not the hash for it (it should be only set automatically when data is assigned).
I tried using __setattr__
/__dict__
, but mongoengine
seems to be doing some attributes magic behind the scene and I couldn’t make it work. I also had an idea to subclass StringField
and use a metaclass to wrap it’s __setattr__
, with similar effect.
How to achieve such a behaviour?
1
As mentioned in comments, I think using Change Streams to update field_b
when field_a
is updated/setted under the hood seems more decent.
On Document
struct level, we should mark field_b
as protected so no unintentional update is performed in codes.
Firstly, It’s hard for interpreter to do something like:
if field_a.is_update(): # pseudo code
# ok we can update field_b inside if statement
field_b = "bbbbb"
field_b = "ccccc" # NO! you can't do it here
Secondly, I don’t think using metaclass
or other stuff to change the behavior of a third-party library is a good idea since a break change from upstream could possibly break everything…
Finally, if you insists, I believe snippet below could be used as a work-around:
from mongoengine.document import Document
from mongoengine import StringField
# pseudo code
class ProtectedField(StringField):
def __set__(self, instance, value):
# we can set field_b based on instance and discard value
# or we can raise error if value is not None
raise AttributeError("can't set attribute to it")
# other possible methods to override
class Class_A(Document):
field_a = StringField()
field_b = ProtectedField()
@property
def field_a(self):
return self._field_a
@field_a.setter
def field_a(self, value):
self._field_a = value