flaskflask-sqlalchemyflask-wtformsflask-loginflask-admin

Flask-admin not recognising current_user in model View


I created my ModelView in flask-admin and want to give role choices to user such that only admin can create user with role manager,admin or user. And user shouldn't have choice to give admin privilidges or as such. I am trying this code but it's giving me:

AttributeError: 'NoneType' object has no attribute 'is_authenticated'

class UserView(ModelView):
    column_exclude_list = ['logs', 'password_hash',]
    form_excluded_columns = ['logs']
    can_edit = True
    if login.current_user or current_user.is_authenticated:
        if login.current_user.role == 'a':
            form_choices = {
            'role': [ ('a', 'Admin'), ('m', 'Manager'), ('u', 'User') ]
            }
        if login.current_user.role == 'm':
            form_choices = {
        'role': [
            ('m', 'Manager'),
            ('u', 'User')
            ]
    }

Any help would be highly appreciated.


Solution

  • Evaluating current_user always returns a NoneType unless it is within a Flask application context. Do something like the following:

    def get_user_roles():
    
        _roles = []
        
        if current_user and current_user.is_authenticated:
            if current_user.role == 'a':
                _roles =  [('a', 'Admin'), ('m', 'Manager'), ('u', 'User')]
            elif login.current_user.role == 'm':
                _roles = [('m', 'Manager'), ('u', 'User')]
    
        return _roles
    
    class UserView(ModelView):
    
        form_choices = {
            'role' : get_user_roles
        }
    

    Single file example. If you run this without logging in you will see only the "User" role in the role choices:

    requirements.txt
    
    Babel==2.11.0
    blinker==1.5
    click==8.1.3
    colorama==0.4.6
    dnspython==2.2.1
    email-validator==1.3.0
    Flask==2.2.2
    Flask-Admin==1.6.0
    Flask-BabelEx==0.9.4
    Flask-Login==0.6.2
    Flask-Mail==0.9.1
    Flask-Principal==0.4.0
    Flask-SQLAlchemy==3.0.2
    Flask-WTF==1.0.1
    greenlet==2.0.1
    idna==3.4
    itsdangerous==2.1.2
    Jinja2==3.1.2
    MarkupSafe==2.1.1
    passlib==1.7.4
    pytz==2022.6
    speaklater==1.3
    SQLAlchemy==1.4.44
    Werkzeug==2.2.2
    WTForms==3.0.1
    

    app.py

    import os
    from datetime import datetime
    from werkzeug.security import generate_password_hash, check_password_hash
    import click
    from flask import Flask, current_app
    from flask_admin import Admin
    from flask_admin.contrib.sqla import ModelView
    from flask_login import current_user, UserMixin, LoginManager
    from flask_sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    login_manager = LoginManager()
    
    
    class User(db.Model, UserMixin):
        __tablename__ = 'users'
    
        id = db.Column(db.Integer, primary_key=True)
    
        first_name = db.Column(db.Unicode(length=255), nullable=False)
        last_name = db.Column(db.Unicode(length=255), nullable=False, index=True)
    
        # Identification Data: email & password
        email = db.Column(db.Unicode(length=254), nullable=False, unique=True)
        password = db.Column(db.Unicode(length=255), nullable=False)
        active = db.Column(db.Boolean(), default=False)
    
        role = db.Column(db.Unicode(length=255), nullable=False)
    
    
    @login_manager.user_loader
    def load_user(user_id):
        return User.get(user_id)
    
    
    basedir = os.path.abspath(os.path.dirname(__file__))
    
    app = Flask(__name__)
    
    app.config['SECRET_KEY'] = '123456790'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'database.db')
    
    db.init_app(app)
    login_manager.init_app(app)
    
    
    @app.cli.command('create-database', short_help='Create sample database')
    def create_database():
    
        """
            Create database
        """
    
        db.drop_all()
        db.create_all()
    
        users = [
            {
                'email': 'paul@example.net',
                'first_name': 'Paul',
                'last_name': 'Cunningham',
                'password': generate_password_hash('pa$$word'),
                'active': True,
                'role': 'a',
            },
            {
                'email': 'jane@example.net',
                'first_name': 'Jane',
                'last_name': 'Smith',
                'password': generate_password_hash('pa$$word'),
                'active': True,
                'role': 'm'
            },
        ]
    
        for user in users:
            _user_db = User(**user)
            db.session.add(_user_db)
    
        db.session.commit()
    
    
    @app.route('/')
    def index():  # put application's code here
        return '<a href="/admin/">Click me to get to Admin!</a>'
    
    
    admin = Admin(app, template_mode="bootstrap3")
    
    
    def get_user_roles():
    
        # default role is a user
        _roles = [('u', 'User')]
    
        if current_user and current_user.is_authenticated:
            if current_user.has_role('a'):
                print('Current user is an admin')
                _roles = [('a', 'Admin'), ('m', 'Manager'), ('u', 'User')]
            elif current_user.has_role('m'):
                print('Current user is a manager')
                _roles = [('m', 'Manager'), ('u', 'User')]
    
        print(f"Roles assigned to role choices: {_roles}")
        return _roles
    
    
    class UserView(ModelView):
    
        column_list = ('first_name', 'last_name', 'email', 'role')
    
        form_columns = column_list
    
        form_choices = {
            'role': get_user_roles
        }
    
    
    admin.add_view(UserView(User, db.session))
    
    
    if __name__ == '__main__':
        app.run()
    

    Run the following command to initialize an SQLite DB.

    flask create-database