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 -~----------~----~----~----~------~----~------~--~---