Amin Etesamian
Amin Etesamian

Reputation: 3699

Sqlalchemy: automatically calculate the value of a column upon another column changed

I want the value of a column (is_dangerous_token) to be calculated using a method. Here is what I have gone so far.

class PassRecovery(DeclarativeBase):
    __tablename__ = 'pass_recoveries'

    id = Column(Integer, primary_key=True)
    account_email_address = Column(Unicode(255), ForeignKey('accounts.email_address'), index=True)
    account = relationship('Account', backref=backref('pass_recoveries', cascade='all, delete-orphan'))
    _is_dangerous_token = Column('is_dangerous_token', Unicode(255))

    def _set_is_dangerous_token(self, account_email_address):
        secret_key = config.get('is_dangerous_key')
        signer = TimestampSigner(secret_key)
        self._is_dangerous_token = signer.sign(account_email_address)

    def _get_is_dangerous_token(self):
        return self._is_dangerous_token

    is_dangerous_token = synonym(
        '_is_dangerous_token',
        descriptor=property(_get_is_dangerous_token, _set_is_dangerous_token)
    )

But the problem is that when I create a row by passing in the email_address, the is_dangerous_token is not generated using the code I wrote. The created row only has the id and the relation to the account and is_dangerous_token has no value in the database. thanks for any help

Upvotes: 1

Views: 533

Answers (1)

pylover
pylover

Reputation: 8055

You have to use synonym on the account_email_address column, because you need it's value to generate the token:

class PassRecovery(DeclarativeBase):
    __tablename__ = 'pass_recoveries'

    id = Column(Integer, primary_key=True)
    _account_email_address = Column(Unicode(255), ForeignKey('accounts.email_address'), index=True)
    account = relationship('Account', backref=backref('pass_recoveries', cascade='all, delete-orphan'))
    is_dangerous_token = Column('is_dangerous_token', Unicode(255))

    def _set_account_email_address(self, account_email_address):
        self._account_email_address = account_email_address

        secret_key = config.get('is_dangerous_key')
        signer = TimestampSigner(secret_key)
        self.is_dangerous_token = signer.sign(self._account_email_address)

    def _get_account_email_address(self):
        return self.account_email_address

    account_email_address = synonym(
        '_account_email_address',
        descriptor=property(_get_account_email_address, _set_account_email_address)
    )

EDIT 1

Assume Persoan.A is a synonym.

session.query(Person).filter(Person.A > 8)

So you can use columns which not exists in database in your query for calculating complex expressions.

But you cannot do this with properties, In your case It's better to simply use python's property instead of synonym, to avoid cost of the sqlalchemy synonym.

Another approach is to use sqlalchemy events, listen for changing the attribute, and do whatever you want when the event is raised.

Upvotes: 2

Related Questions