title: How can I create an encrypted django field that converts data when it's retrieved from the database?
body: <p>I have a custom EncryptedCharField, which I want to basically appear as a CharField when interfacing UI, but before storing/retrieving in the DB it encrypts/decrypts it.</p>
  
  <p>The <a href="https://docs.djangoproject.com/en/dev/howto/custom-model-fields/" rel="noreferrer">custom fields documentation</a> says to:</p>
  
  <ol>
  <li>add <code>__metaclass__ = models.SubfieldBase</code></li>
  <li>override to_python to convert the data from it's raw storage into the desired format</li>
  <li>override get_prep_value to convert the value before storing ot the db.</li>
  </ol>
  
  <p>So you think this would be easy enough - for 2. just decrypt the value, and 3. just encrypt it.</p>
  
  <p>Based loosely on <a href="http://djangosnippets.org/snippets/1095/" rel="noreferrer">a django snippet</a>, and the documentation this field looks like:</p>
  
  <pre><code>class EncryptedCharField(models.CharField):
    """Just like a char field, but encrypts the value before it enters the database, and    decrypts it when it
    retrieves it"""
    __metaclass__ = models.SubfieldBase
    def __init__(self, *args, **kwargs):
      super(EncryptedCharField, self).__init__(*args, **kwargs)
      cipher_type = kwargs.pop('cipher', 'AES')
      self.encryptor = Encryptor(cipher_type)
  
    def get_prep_value(self, value):
       return encrypt_if_not_encrypted(value, self.encryptor)
  
    def to_python(self, value):
      return decrypt_if_not_decrypted(value, self.encryptor)
  
  
  def encrypt_if_not_encrypted(value, encryptor):
    if isinstance(value, EncryptedString):
      return value
    else:
      encrypted = encryptor.encrypt(value)
      return EncryptedString(encrypted)
  
  def decrypt_if_not_decrypted(value, encryptor):
    if isinstance(value, DecryptedString):
      return value
    else:
      encrypted = encryptor.decrypt(value)
      return DecryptedString(encrypted)
  
  
  class EncryptedString(str):
    pass
  
  class DecryptedString(str):
    pass
  </code></pre>
  
  <p>and the Encryptor looks like:</p>
  
  <pre><code>class Encryptor(object):
    def __init__(self, cipher_type):
      imp = __import__('Crypto.Cipher', globals(), locals(), [cipher_type], -1)
      self.cipher = getattr(imp, cipher_type).new(settings.SECRET_KEY[:32])
  
    def decrypt(self, value):
      #values should always be encrypted no matter what!
      #raise an error if tthings may have been tampered with
      return self.cipher.decrypt(binascii.a2b_hex(str(value))).split('\0')[0]
  
    def encrypt(self, value):
      if value is not None and not isinstance(value, EncryptedString):
        padding  = self.cipher.block_size - len(value) % self.cipher.block_size
        if padding and padding &lt; self.cipher.block_size:
          value += "\0" + ''.join([random.choice(string.printable) for index in range(padding-1)])
        value = EncryptedString(binascii.b2a_hex(self.cipher.encrypt(value)))
      return value
  </code></pre>
  
  <p>When saving a model, an error, Odd-length string, occurs, as a result of attempting to decrypt an already decrypted string.  When debugging, it appears as  to_python ends up being called twice, the first with the encrypted value, and the second time with the decrypted value, but not actually as a type Decrypted, but as a raw string, causing the error.  Furthermore get_prep_value is never called.</p>
  
  <p>What am I doing wrong?</p>
  
  <p>This should not be that hard - does anyone else think this Django field code is very poorly written, especially when it comes to custom fields, and not that extensible?  Simple overridable pre_save and post_fetch methods would easily solve this problem.</p>