Search code examples
pythonsqlalchemymarshmallow

How do i send a User object (from Python Flask) back to the frontend (Angular) without sending the password hash and salt?


I am using Flask as a Rest API for my WebApp.

In the frontend i use the User object quite often, which is why i need it from the backend to work with the user data.

My concern is, that the user object has an attribute password, which is obviously also sent to the frontend, when i make a request for a user object.

Should i define another class like UserPublic to send to the frontend and just strip out the password or is there a better way to do this with Flask, SQLAlchemy, Marshmallow?

I'm not sure if it's even a problem sending the password hash+salt to the frontend. I mean, i don't need it there, so why send it? Password check for login purposes is done in the backend anyway.

This is my User class:

class User(db.Model):
    __tablename__ = 'user'
    user_id = db.Column(db.Integer, primary_key=True,autoincrement=True, nullable=False)
    public_id = db.Column(db.String, nullable=False)
    fname = db.Column(db.String, nullable=False)
    lname= db.Column(db.String, nullable=False)
    bday = db.Column(db.Date, nullable=False) 
    street = db.Column(db.String, nullable=False)
    zip = db.Column(db.String, nullable=False) #Zip used
    city = db.Column(db.String, nullable=False)
    country = db.Column(db.String,  nullable=False, default='Germany')
    password = db.Column(db.String, nullable=False)
    admin = db.Column(db.Boolean, default=False)
    email = db.Column(db.String, nullable=False)
    iban = db.Column(db.String)
    bic = db.Column(db.String)
    gender = db.Column(db.CHAR, default='m', nullable=False)

    created_by = db.Column(db.String)
    updated_by = db.Column(db.String)

    membership_status_id = db.Column(db.Integer, db.ForeignKey('membership_status.membership_status_id'))
    member_since = db.Column(db.Date)
    bookings = db.relationship('Booking', backref="User", lazy='select')

Marshmallow Schema:

class UserSchema(ma.SQLAlchemyAutoSchema):
    class Meta:
        model = User
        include_fk = True

This is the endpoint to get a user object:

@app.route('/users/<public_id>', methods=['GET'])
@jwt_required
def get_user(public_id):
    logger.info('Getting user with id: '+str(public_id))
    current_user = User.query.filter_by(public_id=get_jwt_identity()).first()

    if not current_user.admin:
       return jsonify({'message' : 'Not privileged for this action'})

    user = User.query.filter_by(public_id=public_id).first()
    
    if not user:
        return jsonify({'message' : 'No user found with id '+str(public_id)})
    user_schema = UserSchema()
    return user_schema.jsonify(user), 200

Solution

  • See doc about overriding generated fields.

    Here's how to exclude the field from the auto-generated schema:

    class UserSchema(ma.SQLAlchemyAutoSchema):
        class Meta:
            model = User
            include_fk = True
            exclude = ("password", )
    
        # You may want to only exclude id on dump but keep it on load
        # In this case, add it here by calling `auto_field` yourself
        password = ma.auto_field(load_only=True)