title: Alternate Values for MongoDB fields setup via Mongoengine
body: <p>I have a somewhat different need/requirement in how I use mongoengine and wanted to share the below to get any ideas on how to deal with it.</p>
  <p>My requirement is that for some fields in a document, I want to be able to keep two values.  One is a system-default value and the other a user-specified value.  When accessing this field, it should return back a value in the following manner:</p>
  <ul>
  <li>if a user-specified value exists, return that OR</li>
  <li>if a user-specified value doesn’t exist, return the default</li>
  </ul>
  <p>I can not use the default attribute that mongoengine already provides because:</p>
  <ol>
  <li>It is applied to the mongoengine class model rather than the document</li>
  <li>The default value would need to be updated based on system changes in newer versions of the software</li>
  <li>There would not be a memory of what the default is at the document level so it would be difficult to determine if the default needs to be changed for certain documents only</li>
  </ol>
  <p>I thought of creating something called an AlternateValueField, which is based off a MapField.  Below is the code for this new field type:</p>
  <pre><code>import mongoengine
  
  class AlternateValueField(mongoengine.MapField):
      def __init__(self, field=None, *args, **kwargs):
          self.allowed_keys = (
              '_active',  # Active Value - changeable by user
              '_default',  # Default Value - provided by system
          )
          # Remove the field's default since it now gets handled
          # as _default
          self.kwargs_default = kwargs.pop('default', None)
          super().__init__(field=field, *args, **kwargs)
  
      def __get__(self, instance, owner):
          result = super().__get__(instance, owner)
          if isinstance(result, dict):
              if '_active' in result.keys():
                  return result['_active']
              if '_default' in result.keys():
                  return result['_default']
          return result
  
      def __set__(self, instance, value):
          instance_dict = instance._data.get(self.name)
          if isinstance(value, dict):
              if isinstance(instance_dict, dict):
                  # To keep the order (_active followed by _default)
                  new_value = {}
                  for key in self.allowed_keys:
                      if (key in instance_dict.keys()
                              and key not in value.keys()):
                          new_value[key] = instance_dict[key]
                      elif key in value.keys():
                          new_value[key] = value[key]
                  value = new_value
          elif value is not None:
              new_value = {
                  '_active': value,
              }
              if isinstance(instance_dict, dict):
                  if '_default' in instance_dict.keys():
                      new_value['_default'] = instance_dict['_default']
              value = new_value
          else:
              if self.required and self.default is not None:
                  new_value = {
                      '_default': self.kwargs_default,
                  }
                  value = new_value
          self._check_for_allowed_keys(value)
          super().__set__(instance, value)
  
      def _check_for_allowed_keys(self, value):
          if value is None:
              return
          for key in value.keys():
              if key in self.allowed_keys:
                  continue
              err_msg = (
                  f&quot;Key '{key}' is not allowed.&quot;
                  f&quot; Allowed keys: {self.allowed_keys}&quot;
              )
              raise KeyError(err_msg)
  
      def validate(self, value, **kwargs):
          super().validate(value, **kwargs)
          self._check_for_allowed_keys(value)
  </code></pre>
  <p>A document example that uses it:</p>
  <pre><code>class MySchedule(mongoengine.Document):
      my_name = AlternateValueField(mongoengine.StringField())
      sched1 = mongoengine.StringField()
  </code></pre>
  <p>Using this model to create a document:</p>
  <pre><code>&gt;&gt;&gt; s2 = MySchedule(my_name={'_active': 'one', '_default': 'two'}, sched1='yes')
  
  &gt;&gt;&gt; s2.validate()
  
  &gt;&gt;&gt; s2.my_name  # returns just the value (of active or default) instead of dict
  'one'
  
  &gt;&gt;&gt; s2.to_json()
  '{&quot;my_name&quot;: {&quot;_active&quot;: &quot;one&quot;, &quot;_default&quot;: &quot;two&quot;}, &quot;sched1&quot;: &quot;yes&quot;}'
  
  &gt;&gt;&gt; s2.to_mongo()
  SON([('my_name', {'_active': 'one', '_default': 'two'}), ('sched1', 'yes')])
  
  &gt;&gt;&gt; s2._fields
  {'my_name': &lt;__main__.AlternateValueField object at 0x7fd37fbaf1c0&gt;, 'sched1': &lt;mongoengine.fields.StringField object at 0x7fd37f968250&gt;, 'id': &lt;mongoengine.base.fields.ObjectIdField object at 0x7fd378225640&gt;}
  
  &gt;&gt;&gt; s2._data
  {'id': None, 'my_name': {'_active': 'one', '_default': 'two'}, 'sched1': 'yes'}
  
  &gt;&gt;&gt; s2.my_name = 'anotherone'  # works due to the __set__() implementation above
  
  &gt;&gt;&gt; s2.my_name
  'anotherone'
  
  &gt;&gt;&gt; s2._data
  {'id': None, 'my_name': {'_active': 'anotherone', '_default': 'two'}, 'sched1': 'yes'}
  
  &gt;&gt;&gt; s2.my_name['_active'] = 'blah'  # won't work now
  Traceback (most recent call last):
    File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;
  TypeError: 'str' object does not support item assignment
  
  &gt;&gt;&gt; s2._data['my_name']['_active'] = 'blah'  # will work
  
  &gt;&gt;&gt; s2.my_name
  'blah'
  
  &gt;&gt;&gt; s2._data
  {'id': None, 'my_name': {'_active': 'blah', '_default': 'two'}, 'sched1': 'yes'}
  </code></pre>
  <p>My issue is, I would like to be able to use these cases:</p>
  <ul>
  <li>s2.my_name   # works - returns the _active value or _default value, not dict of them</li>
  <li>s2.my_name = 'something'  # works - via the <code>__set__()</code> implementation</li>
  <li>s2.my_name['_active'] = 'blah'  # causes error as shown above</li>
  </ul>
  <p>The 3rd case above is what I'd like to be able to do without causing an error.</p>
  <p>I tried the following in an attempt to get around it:</p>
  <ul>
  <li>overriding <code>__get__()</code>, which doesn't work because it's job is done prior to recognizing there is a getitem call</li>
  <li>thought about overriding <code>BaseDict.__getitem__()</code> to recognize this, but that won't work for when there is no getitem call</li>
  </ul>
  <p>I would need something after the AlternateField's <code>__get__()</code> call and before <code>BaseDict's __getitem__()</code> call to recognize whether a getitem is following it or not.</p>
  <p>Any thoughts on best way to get around this?  Obviously, the above may just all be a bad idea.  Any alternatives (pardon the pun :)) on creating fields with alternative values that can be changed on a per-document basis and remembered across restarts of the software?  Fields can have all the types mongoengine supports, but mostly will be string, int, and potentially, list.</p>