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?
2
Answers
As mentioned in comments, I think using Change Streams to update
field_b
whenfield_a
is updated/setted under the hood seems more decent.On
Document
struct level, we should markfield_b
as protected so no unintentional update is performed in codes.Firstly, It’s hard for interpreter to do something like:
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:
Approach Using a Custom Property for
field_a
and Signal to Handle Field ModificationExplanation:
Custom Property for
field_a
:field_a
. Thefield_a
setter automatically computes a hash (or any derived value) and assigns it tofield_b
wheneverfield_a
is updated.Prevent Direct Modification of
field_b
:save
method, we check iffield_b
has been modified directly by the user (it’s part of the_changed_fields
list). If it has, an exception is raised, preventing the user from modifyingfield_b
.Hash Function:
hashlib.sha256
function is used as an example to compute a hash forfield_a
. You can replace this with any logic depending on your use case.Example Usage: