pennine
pennine

Reputation: 59

Getting an AttributeError when using a setter in SQLAlchemy

I want to automatically hash passwords before storing them.

from sqlalchemy.ext.hybrid import hybrid_property

from blog import db, bcrypt
...
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    email = db.Column(db.String(64), nullable=False)
    _password_hash = db.Column(db.String(128))

    @hybrid_property
    def password(self):
        return self._password_hash

    @password.setter
    def _set_password(self, plain):
        self._password_hash = bcrypt.generate_password_hash(plain)

Manually setting the _password_hash property works fine:

>>> new = User(name='johndoe', email='[email protected]', _password_hash='strongpassword')
>>> new.password
'strongpassword'

But when I try to use the password property I get this error:

>>> new.password = 'strongpassword'
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/ext/hybrid.py", line 899, in __set__
    raise AttributeError("can't set attribute")
AttributeError: can't set attribute

Another example:

>>> new = User(name='johndoe', email='[email protected]', password='strongpassword')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "<string>", line 4, in __init__
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/orm/state.py", line 441, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 153, in reraise
    raise value
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/orm/state.py", line 438, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/ext/declarative/base.py", line 842, in _declarative_constructor
    setattr(self, k, kwargs[k])
  File "/home/toki/.local/lib/python3.8/site-packages/sqlalchemy/ext/hybrid.py", line 899, in __set__
    raise AttributeError("can't set attribute")
AttributeError: can't set attribute

How do I fix it? Why it doesn't work?

Upvotes: 1

Views: 687

Answers (1)

KingDarBoja
KingDarBoja

Reputation: 1053

Not really used to SQLAlchemy and had no idea about hybrid properties, but by judging on how it is structured, I think the setter method should have the same property name like as the hybrid one.

Something like:

class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), nullable=False)
    email = db.Column(db.String(64), nullable=False)
    _password_hash = db.Column(db.String(128))

    @hybrid_property
    def password(self):
        return self._password_hash

    @password.setter
    def password(self, plain):
        self._password_hash = bcrypt.generate_password_hash(plain)

Again, my knowledge is limited on SQLAlchemy (using psycopg2 for my projects at the moment).

Upvotes: 4

Related Questions