Search code examples
flaskflask-sqlalchemyflask-restplus

Recieving the error 'Expected bytes' when trying to update a DB entry


My API allows a user to update their user details by with a PUT request to the /User endpoint.

However, when I try to send an update. It is failing when trying to save the password with the exception Expected Bytes.

Below is my route:

@UserNS.expect(UserModel)
    @token_required
    def put(self):
        """Updates the user details associated with the API token."""
        token = request.headers['Token']
        data = request.get_json()
        if not data:
            return jsonify({'message': 'Please enter some user data'})
        else:
            update_user = User.query.filter_by(api_token=token).first()
            print("Updating user details")
            print("Current: " + update_user.username + " " + update_user.email)
            print("New: " + data["Username"] + " " + data["Email Address"])
            try:
                update_user.username = str(data["Username"])
                update_user.email = str(data['Email Address'])
                print("Trying to save password...")
                update_user.set_password(data['Password'])
                print("uh oh...")
                db.session.commit()
                return {'Message': 'Record updated!'}
            except Exception as e:
                print(e)
                return {'Message': 'Username or email address is invalid!'}

Below is my API model:

UserModel = api.model('User', {'Username': fields.String(), 'Email Address': fields.String(), 'Password': fields.String()})

And my SQLAlchemy model:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True, unique=True)
    api_token = db.Column(db.String(50), unique=True)
    username = db.Column(db.String(128), index=True, unique=True)
    email = db.Column(db.String(128), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    todos = db.relationship('Todo', backref='owner', lazy='dynamic')

I am using very similar data to register the account. So not sure wht it is not saving the new data.

Edit: I think the problem is Python is converting the update_user to a non bytes object here:

update_user = User.query.filter_by(api_token=token).first()

So I need a way to read to make the query and store it for manipulation. Without transforming it to a non-bytes object.

edit2:

Flask-login is being used to store the password using the below definition:

def set_password(self, password):
    self.password_hash = generate_password_hash(password)

Solution

  • The underlying werkzeug.security.generate_password_hash method expects a str as input. If you pass it an invalid type such as an int, you get the TypeError: Expected bytes exception. Method docs here.

    As noted in my comment above, there are tutorials that show working examples that ensure the proper input types on the form.