简体   繁体   中英

Python SQLAlchemy - How to update database using object within model by a method?

I am writing a simple login which has one time passwords using the PyOTP library and SQLalchemy and a class I am using which works fine.

I want to save the current TOTP into the database once validated using this:

session = Session()
self.lastOtp = currentTotp
session.commit()
session.close()

But it doesn't update the database at all. I think it's because the method doesn't know it's the specific user object I am trying to update but I've tried adding self to various lines to figure it out but cannot. I've the following working:

methodSession = Session()
currentUser = methodSession.query(User).filter_by(emailAddress=self.emailAddress).one()
currentUser.lastOtp = currentTotp
methodSession.commit()
methodSession.close()
self.lastOtp = currentTotp

But that means another database query and I already have a User object which I can access as self.

This is the full user class

# user class
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    firstName = Column(String(64))
    lastName = Column(String(64))
    emailAddress = Column(String(128), index=True, unique=True)
    passwordHash = Column(String(128))
    secretKey = Column(BLOB)
    lastOtp = Column(String(128))

    def __repr__(self):
        return '<User {}>'.format(self.emailAddress)

    def set_password(self, password):
        # function to set password hash
        self.passwordHash = generate_password_hash(password)

    def check_password(self, password):
        # function to check password returning true or false
        return check_password_hash(self.passwordHash, password)

    def set_secret_key(self):
        # function to set the secure secret key
        pass

    def get_secret_key(self):
        # function to return the secure secret key
        pass
        
    def check_secret_key(self, secret_key):
        # function to check the otp returning true or false
        totp = pyotp.TOTP(self.get_secret_key())
        currentTotp = totp.now()
        if self.lastOtp == currentTotp:
            return False
        if totp.verify(secret_key):
            session = Session()
            self.lastOtp = currentTotp
            session.commit()
            session.close()
            return True
        return False

Expected behaviour:

session = Session()
myUser = session.query(User).filter_by(emailAddress='john@smith.com').one()
print("Last OTP: ", myUser.lastOtp)
>>> 987568
myUser.check_secret_key('123456')
>>> True
print("Last OTP: ", myUser.lastOtp)
>>> 123456
myUser.check_secret_key('123456')
>>> False

You need to add the object you want to save to the SQLAlchemy session. I do not see in your code where you are doing that. Specifically, here:

if totp.verify(secret_key):
            session = Session()
            self.lastOtp = currentTotp
            session.commit()
            session.close()
            return True

you are creating the Session, then commit and close it, without adding an instance of the object you want to save to it. You are missing a session.add(...) call.

If the User instance is already associated with a session, that session may be retrieved using Session.object_session :

def check_secret_key(self, secret_key):
    # function to check the otp returning true or false
    totp = pyotp.TOTP(self.get_secret_key())
    currentTotp = totp.now()
    if self.lastOtp == currentTotp:
        return False
    if totp.verify(secret_key):
        session = Session.object_session(self)
        self.lastOtp = currentTotp
        session.commit()
        return True
    return False

Note that committing this session will commit any other changes already associated with it.

Looks like Matei's comment means this is the only solution:

def check_secret_key(self, secret_key):
    # function to check the otp returning true or false
    totp = pyotp.TOTP(self.get_secret_key())
    currentTotp = totp.now()
    if self.lastOtp == currentTotp:
        return False
    if totp.verify(secret_key):
        methodSession = Session()
        currentUser = methodSession.query(User).filter_by(emailAddress=self.emailAddress).one()
        currentUser.lastOtp = currentTotp
        methodSession.commit()
        methodSession.close()
        self.lastOtp = currentTotp
        return True
    return False

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM