mapper extensions are established during the mapper() phase:

mapper(someclass, sometable, extension=BycryptMapperExtension())


On Jan 1, 2009, at 1:35 PM, Kless wrote:

>
> I've been trying create the extension. Here is the code with a test:
>
>  http://paste.pocoo.org/show/97502/ - Extension
>  http://paste.pocoo.org/show/97503/ - Test
>
> I need help to solve this little issue:
>
> entity._descriptor.add_mapper_extension(BcryptMapperExtension())
> AttributeError: 'str' object has no attribute '_descriptor'
>
> Does could review the extension code?
>
>
> On 22 dic 2008, 03:40, Michael Bayer <mike...@zzzcomputing.com> wrote:
>> oh, yeah for that recipe you'd have to use a flag on the mapper()
>> called "batch=False".    But don't use that, its inefficient.
>>
>> So you can also create your type to detect a special value from the
>> mapper extension:
>>
>> def before_insert(self, ....):
>>         instance.password = (instance.password, True)
>>
>> class MyType(...):
>>         ...
>>
>>         def process_bind_param(self, value, dialect):
>>                 if isinstance(value, tuple):
>>                         return hasher.create(value[0], value[1])
>>                 ....
>>
>> But since this is really an instance-level business rule, a straight
>> descriptor and no custom type is definitely how I'd go on this one.
>>
>> On Dec 21, 2008, at 6:47 PM, Kless wrote:
>>
>>
>>
>>> Thank you for detailed answer.
>>
>>> Here are any thoughts:
>>
>>> 1) The before_insert was being omitted while I was inserting records
>>> using an insert statement --that's logical because it was bypassing
>>> the ORM--.
>>
>>> 2) It's necessary to use *session.commit()* after of each record
>>> because else it will have the value of 'instance.admin' of the last
>>> record which will be used for all records to commit.
>>
>>> 3) I believe that it isn't necessary use *after_insert*.
>>
>>> On 21 dic, 17:00, Michael Bayer <mike...@zzzcomputing.com> wrote:
>>>> On Dec 21, 2008, at 6:53 AM, Kless wrote:
>>>>> I'm trying to build a custom type [1] to manage the bcrypt hashes
>>>>> [2].
>>
>>>>> ---------------
>>>>> from bcrypt_wrap import password
>>>>> from sqlalchemy import types
>>
>>>>> class Bcrypt(types.TypeDecorator):
>>>>>  """Stores a bcrypt hash of a password."""
>>>>>  impl = types.String #(60)
>>>>>  hasher = password.Password()
>>
>>>>>  def process_bind_param(self, value, dialect):
>>>>>    return hasher.create(value)
>>>>> ---------------
>>
>>>>> And I've any doubts:
>>
>>>>> 1) Since that the hash length is always 60, is there that use the
>>>>> next?
>>>>>  impl = types.String(60)
>>
>>>>> 2) The bcryptWrap API [3] (line 53) lets call to 'create' with
>>>>> arguments 'cost' and 'admin' (boolean). Then,
>>
>>>>>  a) I would that the cost could be passed from the column
>>>>> definition,
>>>>> i.e.
>>>>>      password = sqlalchemy.Column(types.Bcrypt, cost=12)
>>>>> or
>>>>>      password = sqlalchemy.Column(types.Bcrypt(cost=12))
>>
>>>>>  b) This would be more hard, but I would to pass the 'admin'
>>>>> argument
>>>>> when an object is created
>>
>>>>>    u = model.User()
>>>>>    u.login = u'foo'
>>>>>    u.password = u'bar'
>>>>>    u.admin = True
>>
>>>>>    And sou could be call:
>>>>>        return hasher.create(value, admin=True)
>>
>>>> the TypeDecorator's __init__ method just calls the impl class
>>>> immediately, so the best approach is like:
>>
>>>> class Bcrypt(types.TypeDecorator):
>>>>      """Stores a bcrypt hash of a password."""
>>>>      impl = types.String
>>>>      hasher = password.Password()
>>
>>>>      def __init__(self, cost):
>>>>          self.cost = cost
>>>>          types.TypeDecorator.__init__(self, 60)
>>
>>>>      def process_bind_param(self, value, dialect):
>>>>          return hasher.create(value)
>>
>>>> then you can instantate like:
>>
>>>> Column('foo', Bcrypt(cost=12))
>>
>>>> For b, the type object and the ExecutionContext which ultimately
>>>> calls
>>>> its process_bind_param method are unaware of the ORM or the ORM-
>>>> specific context in which its called during INSERT/UPDATE.   If you
>>>> wanted to keep the logic within your type like that you'd have to
>>>> integrate to a thread-local variable that is configured within an  
>>>> ORM
>>>> plugin, something like:
>>
>>>> hasher_status = threading.local()
>>
>>>> class MyMapperExt(MapperExtension):
>>>>      def before_insert(self, ...):
>>>>          hasher_status.admin = instance.admin
>>
>>>>      def after_insert(self, ...)
>>>>          del hasher_status.admin
>>
>>>> mapper(MyClass, table, ext=MyMapperExt)
>>
>>>> class Bcrypt(types.TypeDecorator):
>>>>      ...
>>
>>>>      def process_bind_param(self, value, dialect):
>>>>          return hasher.create(value, getattr(hasher_status,  
>>>> 'admin',
>>>> False))
>>
>>>> Alternatively, you could keep the logic within the ORM using  
>>>> either a
>>>> "valdiator" or a descriptor, as described 
>>>> athttp://www.sqlalchemy.org/docs/05/mappers.html#changing-attribute-beh
>>>> ...
>>>>   .  It depends on if you'd like your Bcrypt type to work with  
>>>> direct
>>>> SQL expression language use or not.
> >


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to