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
3 Answers
Reset to default 6I 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条)