On Wed, 2007-07-25 at 23:43 +0000, z0n3z00t wrote:
> > That's not a great answer, but in the meantime, you might want to take
> > a look at the lazy instantiation in the GIS branch[1]. That code isn't
> > exactly what you want, but it might help get you started on a
> > descriptor-based approach to do what you're asking for until there's a
> > proper solution.
> 
> Thanks Marty - that was exactly what I was looking for.  Using a proxy
> with descriptors solved my problem - what a nifty, sneaky little
> trick!
> 
> I had some difficulty determining the context of the __set__ calls -
> on loading from the db, the inbound value is the encrypted db value
> (which needs to be encrypted only then), or due to a user changing the
> value (in clear text - in which case the setter should not decrypt the
> value) - and therefore the flag.
> 
> Here's my take on it:
> 
> class CharEncryptProxy(object):
>     def __init__(self, field):
>         self._field = field
>         self._decrypted = False
> 
>     def __get__(self, obj, type=None):
>         return obj.__dict__[self._field.attname]
> 
>     def __set__(self, obj, value):
>         # only decrypt first time if set by db
>         if (not self._decrypted) and (value != ''):
>             obj.__dict__[self._field.attname] =
> self._field._decrypt(value)
>         else:
>             obj.__dict__[self._field.attname] = value
>         self._decrypted = True
> 
> class CharEncryptField(models.CharField):
>     def _encrypt(self, value):
>         if value is not None:
>             value = ''.join([chr(ord(char)+1) for char in str(value)])
>         return value
> 
>     def _decrypt(self, value):
>         if value is not None:
>             value = ''.join([chr(ord(char)-1) for char in str(value)])
>         return value
> 
>     def get_internal_type(self):
>         return "CharField"
> 
>     def get_db_prep_save(self, value):
>         value = self._encrypt(value)
>         return super(CharEncryptField,
> self).get_db_prep_save(value)
> 
>     def contribute_to_class(self, cls, name):
>         super(CharEncryptField, self).contribute_to_class(cls, name)
>         # override with proxy
>         setattr(cls, self.attname, CharEncryptProxy(self))
> 
> 
> 
> Works like a charm - thanks again for putting me on the right track!

I've been following this thread in the background. Modulo any small
bugs, this looks like exactly the right approach. As you've worked out,
all the bits for subclassing are present. The only slightly painful
thing is having to work out the __set__ and __get__ constructs and
realising that a wrapper field, as you've done, can make things easier.
That's the part I want to automate a bit. Probably via metaclassing so
that it doesn't interfere with whatever Field subclass you are
inheriting from. I'm writing the code at the moment, having had a few
minutes this week to chew over approaches with Jacob and Jeremy Dunck
whilst we were in the same location.

What you've written won't become obsolete in any way, either, since it's
exactly what we're doing to do under the covers and Django will just be
providing a convenience shortcut. You can always ignore the shortcut and
go directly to writing __set__ and __get__ functions if you need to.

Regards,
Malcolm


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to