flask - `AttributeError: 'tuple' object has no attribute 'items' when creating User object in Ad

I'm encountering an error when trying to create a User object from the Admin page using Flask-Admi

I'm encountering an error when trying to create a User object from the Admin page using Flask-Admin with SQLAlchemy. The error occurs in link http://127.0.0.1:5000/admin/user/new/?url=/admin/user/ is:

AttributeError: 'tuple' object has no attribute 'items'

The issue arises when I attempt to access the page for adding a new user in the admin panel. I believe this is related to the way I'm passing enum values to the form, as I am using an Enum class for the role and gender fields in the User model. in models.py:

class Gender(Enum):
    MALE = 'male'
    FEMALE = 'female'

class UserRole(Enum):
    ADMIN = 'admin'
    PATIENT = 'patient'

class User(db.Model, UserMixin):
    id = Column(Integer, autoincrement=True, primary_key=True)
    username = Column(String(50), unique=True, nullable=False)
    password = Column(String(100), nullable=False)
    role = Column(EnumType(UserRole), nullable=False)
    name = Column(String(100))
    birth = Column(Date, default=datetime.date.today)
    phone = Column(String(14), default="2134215567")
    email = Column(String(100), unique=True)
    gender = Column(EnumType(Gender))
    address = Column(String(100), default="address")
    avatar = Column(String(100),
                    default=".jpg")

    def __str__(self):
        return self.username
class Admin(db.Model):
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    def __str__(self):
        return User.query.get(self.id).name

class Patient(db.Model):
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    def __str__(self):
        return User.query.get(self.id).name

class Product(db.Model):
    id = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(String(50), nullable=True )
    price = Column(Integer, nullable=True)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()


in admin.py

from clinic import app, db
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from clinic.models import User, UserRole, Product, Gender
from flask_login import current_user

admin = Admin(app=app, name="Private Clinic", template_mode = 'bootstrap4')

class AuthenticatedView(ModelView):
    def is_accessible(self):
        return current_user.is_authenticated and current_user.role == UserRole.ADMIN



class MyUserView(ModelView):
    column_list = ['id', 'name', 'phone', 'username', 'email', 'address', 'role']
    column_searchable_list = ['username', 'name', 'phone']
    column_editable_list = ['name', 'role', 'gender']
    can_export = True
    column_filters = ['role']
    column_labels = {
        'id': 'ID',
        'name': 'fullname',
        'phone': 'SĐT',
        'username': 'user name',
        'email': 'Email',
        'address': 'address',
        'role': 'role',
    }


admin.add_view(MyUserView(User, db.session))
admin.add_view(ModelView(Product, db.session))

in init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import cloudinary
from flask_login import LoginManager


app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = 'mysql+pymysql://root:12345@localhost/myclinic?charset=utf8mb4'
app.secret_key = '^%^&%^(*^^&&*^$%((^^&$$&^'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
app.config["PAGE_SIZE"] = 4

cloudinary.config(
       cloud_name = 'mycloud',
       api_key = 'api_key',
       api_secret = 'api_secret'
)

db = SQLAlchemy(app)
login = LoginManager(app)

I suspect that this issue is related to the Enum fields in my User model. Before this, I tested a Product class without using Enum, and it worked fine without any errors.

Could someone please help me understand why I'm getting this error with Enum fields in Flask Admin, while the code works fine without them?

Thanks in advance!

I'm encountering an error when trying to create a User object from the Admin page using Flask-Admin with SQLAlchemy. The error occurs in link http://127.0.0.1:5000/admin/user/new/?url=/admin/user/ is:

AttributeError: 'tuple' object has no attribute 'items'

The issue arises when I attempt to access the page for adding a new user in the admin panel. I believe this is related to the way I'm passing enum values to the form, as I am using an Enum class for the role and gender fields in the User model. in models.py:

class Gender(Enum):
    MALE = 'male'
    FEMALE = 'female'

class UserRole(Enum):
    ADMIN = 'admin'
    PATIENT = 'patient'

class User(db.Model, UserMixin):
    id = Column(Integer, autoincrement=True, primary_key=True)
    username = Column(String(50), unique=True, nullable=False)
    password = Column(String(100), nullable=False)
    role = Column(EnumType(UserRole), nullable=False)
    name = Column(String(100))
    birth = Column(Date, default=datetime.date.today)
    phone = Column(String(14), default="2134215567")
    email = Column(String(100), unique=True)
    gender = Column(EnumType(Gender))
    address = Column(String(100), default="address")
    avatar = Column(String(100),
                    default="https://res.cloudinary/dwvkjyixu/image/upload/v1715592288/avatars/pzh51raolcegilwmhm4g.jpg")

    def __str__(self):
        return self.username
class Admin(db.Model):
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    def __str__(self):
        return User.query.get(self.id).name

class Patient(db.Model):
    id = Column(Integer, ForeignKey(User.id), primary_key=True)

    def __str__(self):
        return User.query.get(self.id).name

class Product(db.Model):
    id = Column(Integer, autoincrement=True, primary_key=True)
    name = Column(String(50), nullable=True )
    price = Column(Integer, nullable=True)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()


in admin.py

from clinic import app, db
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from clinic.models import User, UserRole, Product, Gender
from flask_login import current_user

admin = Admin(app=app, name="Private Clinic", template_mode = 'bootstrap4')

class AuthenticatedView(ModelView):
    def is_accessible(self):
        return current_user.is_authenticated and current_user.role == UserRole.ADMIN



class MyUserView(ModelView):
    column_list = ['id', 'name', 'phone', 'username', 'email', 'address', 'role']
    column_searchable_list = ['username', 'name', 'phone']
    column_editable_list = ['name', 'role', 'gender']
    can_export = True
    column_filters = ['role']
    column_labels = {
        'id': 'ID',
        'name': 'fullname',
        'phone': 'SĐT',
        'username': 'user name',
        'email': 'Email',
        'address': 'address',
        'role': 'role',
    }


admin.add_view(MyUserView(User, db.session))
admin.add_view(ModelView(Product, db.session))

in init.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import cloudinary
from flask_login import LoginManager


app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = 'mysql+pymysql://root:12345@localhost/myclinic?charset=utf8mb4'
app.secret_key = '^%^&%^(*^^&&*^$%((^^&$$&^'
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True
app.config["PAGE_SIZE"] = 4

cloudinary.config(
       cloud_name = 'mycloud',
       api_key = 'api_key',
       api_secret = 'api_secret'
)

db = SQLAlchemy(app)
login = LoginManager(app)

I suspect that this issue is related to the Enum fields in my User model. Before this, I tested a Product class without using Enum, and it worked fine without any errors.

Could someone please help me understand why I'm getting this error with Enum fields in Flask Admin, while the code works fine without them?

Thanks in advance!

Share Improve this question edited Nov 21, 2024 at 6:16 Phoe asked Nov 21, 2024 at 6:13 PhoePhoe 712 bronze badges 1
  • 1 Post the complete traceback. – deceze Commented Nov 21, 2024 at 6:19
Add a comment  | 

3 Answers 3

Reset to default 6

I experienced this exact error when creating a new User model or editing a User model with Flask-Admin. I solved this by adding WTForms==3.1.2 to my requirements.txt. This regression was introduced in WTForms==3.2.1.

The traceback was:

Traceback (most recent call last):
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 1478, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 1458, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 1455, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 869, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 867, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask/app.py", line 852, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask_admin/base.py", line 69, in inner
    return self._run_view(f, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask_admin/base.py", line 369, in _run_view
    return fn(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask_admin/model/base.py", line 2149, in edit_view
    form = self.edit_form(obj=model)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask_admin/model/base.py", line 1340, in edit_form
    return self._edit_form_class(get_form_data(), obj=obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/form.py", line 209, in __call__
    return type.__call__(cls, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/flask_admin/form/__init__.py", line 22, in __init__
    super(BaseForm, self).__init__(formdata=formdata, obj=obj, prefix=prefix, **kwargs)
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/form.py", line 281, in __init__
    super().__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/form.py", line 49, in __init__
    field = meta.bind_field(self, unbound_field, options)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/meta.py", line 28, in bind_field
    return unbound_field.bind(form=form, **options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/fields/core.py", line 387, in bind
    return self.field_class(*self.args, **kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/server/venv/lib/python3.12/site-packages/wtforms/fields/core.py", line 133, in __init__
    for k, v in flags.items():
                ^^^^^^^^^^^^^^
AttributeError: 'tuple' object has no attribute 'items'x

Joey's answer presents a good work around for those needing a quick fix--just switch to an older version of WTForms. I dug a little further on this and found that in my case, this exception was due to using a version of the RequiredIf validator found here.

Looks like WTForms dropped support for field_flags tuples in version 3.2.0, as seen here. Apparently it has been deprecated for some time. Since WTForms is no longer converting the tuple to a dict for us, we of course have this exception where tuples don't have a .items() method.

It may be slightly more work to change multiple classes, but I found an easy fix for this in my case is to simply use a dict for field_flags. E.g.,

field_flags = {"requiredif": True}

instead of the older (deprecated) field_flags = ("requiredif",). I believe field flags are also not strictly required for validators, so another option is to remove this attribute if you're not using it.

I had the exact same error. pip3 install wtforms==2.3.0 worked form me.

see github docs (installation requirements) here https://github/pallets-eco/flask-admin/blob/master/requirements-skip/tests-min.txt

Tested with version 3.1.2 and works fine

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742310572a4419810.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信