-1

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.

  • helpful link :- https://stackoverflow.com/questions/46723767/how-to-get-current-user-when-implementing-python-flask-security – Ajay K Dec 02 '22 at 12:04
  • Thanks @AjayK but as you can see I am running the code on __init__.py and for some reason it throws error NoneObject has no atttribute. – Yusharth Singh Dec 02 '22 at 12:07
  • I don't know the exact logic. But you can try https://stackoverflow.com/questions/43815930/flask-admin-how-to-get-current-users-other-infobesides-id-name-email – Ajay K Dec 02 '22 at 12:44

1 Answers1

0

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
pjcunningham
  • 7,676
  • 1
  • 36
  • 49
  • thanks for the suggestion but it didn't work for me. The error still persists. Also, I called the get_user_role function still the result is the same. I regret using flask-admin a bit. Although thanks for clarifying my hunch regarding the application context, any other suggestions? – Yusharth Singh Dec 05 '22 at 09:40
  • 1
    @YusharthSingh - I added a single file example of it working. – pjcunningham Dec 05 '22 at 11:54