Abdulelah
Abdulelah

Reputation: 691

flask-sqlalchemy with PostgreSQL enum value retrieved as a list

I have the weirdest behavior while using flask-sqlalchemy, I have a User model and I have an enum with the user role as userrole, it has the following values ('vendor', 'vendor_admin', 'admin', 'collector', 'retailer'), the weird thing is that whenever I query the users I get all the roles as string except the admin for some reason it comes as a list with the value admin.

I've deleted the database and re-migrated everything from scratch but still.

Here is my User model:

from app import db, ma
from marshmallow_enum import EnumField
import enum
import bcrypt


class UserRoleEnum(enum.Enum):
    admin = 'admin',
    collector = 'collector'
    retailer = 'retailer'
    vendor = 'vendor'
    vendor_admin = 'vendor_admin'


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(), nullable=False)
    mobile = db.Column(db.String(), nullable=False, unique=True)
    username = db.Column(db.String(), unique=True, nullable=False)
    password = db.Column(db.TEXT(), nullable=False)
    is_active = db.Column(db.Boolean(), default=False)
    role = db.Column(db.Enum(UserRoleEnum), nullable=False)
    created_on = db.Column(db.DateTime, server_default=db.func.now())
    updated_on = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now())

    # relations

    #related fields
    organization_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=True)
    invoices = db.relationship('Invoice', backref='user')

    def __init__(
            self,
            name,
            username,
            mobile,
            password,
            role,
            is_active,
            organization_id = None
    ):
        self.name = name
        self.username = username
        self.mobile = mobile
        self.role = role
        self.is_active = is_active
        self.organization_id = organization_id
        self.password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode()

    def __repr__(self):
        return "<id %s>" % self.id


class UserSchema(ma.SQLAlchemyAutoSchema):
    role = EnumField(UserRoleEnum, by_value=True)
    class Meta:
        exclude = ['password']
        model = User
        load_instance = True

Here is the login endpoint:

@auth_routes.route('login', methods=['POST'])
def login():
    payload = request.json

    if not payload.get('mobile') or not payload.get('password'):
        return "Mobile and Password are required", 400

    user = User.query.filter_by(mobile=payload.get('mobile')).first()

    if not user:
        return "User not found", 404

    if bcrypt.checkpw(payload.get('password').encode('utf-8'), user.password.encode('utf-8')):

        organization = None

        if user.organization_id:
            organization = OrganizationSchema().dump(Organization.query.get(user.organization_id)).data

        token = create_access_token(identity={
            'id': user.id,
            'username': user.username,
            'mobile': user.mobile,
            'is_active': user.is_active,
            'role': user.role.value,
            'organization': organization
        }, expires_delta=timedelta(days=0) + timedelta(days=365))
        return {
            "token": token
        }
    else:
        return "Error on password"

Here is an example from the terminal:

>>> user = User.query.get(1)
>>> user.role.value
('admin',)
>>> user = User.query.get(2)
>>> user.role.value
'vendor_admin'

Upvotes: 0

Views: 1412

Answers (1)

Rugnar
Rugnar

Reputation: 3122

You have a trailing comma so admin value is a tuple

class UserRoleEnum(enum.Enum):
    admin = 'admin',

Remove it and all will be fine

Upvotes: 3

Related Questions