我正在尝试使用 Flask_security_too 和 Flask_admin 构建 Flask 应用程序。我需要将 1 个用户链接到 1 台设备,但 1 台设备可以有多个版本:
class Users(db.Model, UserMixin):
__tablename__ = "users"
user_id = Column(Integer, primary_key=True)
username = Column(String(100), unique=True, index=True)
password = Column(String(80))
device_name = Column(String, ForeignKey("devices.name"))
active = Column(Boolean())
roles = relationship(
"Roles", secondary="roles_users", backref=backref("users", lazy=True)
)
fs_uniquifier = Column(
String(255),
unique=True,
nullable=False,
# Line below necessary to avoid "ValueError: Constraint must have a name"
name="unique_fs_uniquifier_constraint",
)
devices = relationship("Devices", backref=backref("users", lazy=True))
def versions(self):
if self.devices:
return ", ".join([release.version for device in self.devices for release in device.releases])
else:
return ""
def __repr__(self):
return self.username
class Roles(db.Model, RoleMixin):
...
class Devices(db.Model):
__tablename__ = "devices"
device_id = Column(Integer, primary_key=True)
name = Column(String(20), unique=True)
country = Column(String(3), nullable=True)
releases = relationship("Releases", backref=backref("devices", lazy=True))
def __repr__(self):
return f"{self.name.capitalize()}, country: ({self.country})"
class Releases(db.Model):
__tablename__ = "releases"
release_id = Column(Integer, primary_key=True)
device_id = Column(Integer, ForeignKey("devices.device_id"))
version = Column(String(20)) # e.g. 8.0.122
flag_visible = Column(Boolean())
def __repr__(self):
return f"{self.version}"
然后我为 Flas_admin 编写了用户列表的可视化代码:
class UserAdminView(ModelView):
# Actual columns' title as seen in the website
column_list = ("username", "versions", "active", "roles")
# Link the columns' title and the model class attribute, so to make data sortable
column_sortable_list = (
"username",
("versions", "device_name"),
"active",
("roles", "roles.name"),
)
def is_accessible(self):
return (
current_user.is_active
and current_user.is_authenticated
and any(role.name == "administrator" for role in current_user.roles)
)
def _handle_view(self, name):
if not self.is_accessible():
return redirect(url_for("security.login"))
@staticmethod
def _display_roles(view, context, model, name):
return ", ".join([role.name.capitalize() for role in model.roles])
@staticmethod
def _display_versions(view, context, model, name):
return model.versions()
column_formatters = {"roles": _display_roles, "versions": _display_versions}
admin.add_view(UserAdminView(Users, db.session))
但是,我收到这个(双重)错误:
Traceback (most recent call last):
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/base.py", line 369, in _run_view
return fn(self, *args, **kwargs)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/model/base.py", line 2029, in index_view
return self.render(
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/base.py", line 308, in render
return render_template(template, **kwargs)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/templating.py", line 152, in render_template
return _render(app, template, context)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/templating.py", line 133, in _render
rv = template.render(context)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/jinja2/environment.py", line 1301, in render
self.environment.handle_exception()
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/jinja2/environment.py", line 936, in handle_exception
raise rewrite_traceback_stack(source=source)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/templates/bootstrap3/admin/model/list.html", line 6, in top-level template code
{% import 'admin/model/row_actions.html' as row_actions with context %}
File "/home/arpaia/customer-webpage-rewrite/app/templates/admin/master.html", line 1, in top-level template code
{% extends 'admin/base.html' %}
File "/home/arpaia/customer-webpage-rewrite/app/templates/admin/base.html", line 38, in top-level template code
{% block page_body %}
File "/home/arpaia/customer-webpage-rewrite/app/templates/admin/base.html", line 78, in block 'page_body'
{% block body %}{% endblock %}
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/templates/bootstrap3/admin/model/list.html", line 68, in block 'body'
{% block model_list_table %}
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/templates/bootstrap3/admin/model/list.html", line 116, in block 'model_list_table'
{% block list_row scoped %}
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/templates/bootstrap3/admin/model/list.html", line 146, in block 'list_row'
{{ get_value(row, c) }}
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/model/base.py", line 1877, in get_list_value
return self._get_list_value(
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/model/base.py", line 1836, in _get_list_value
value = column_fmt(self, context, model, name)
File "/home/arpaia/customer-webpage-rewrite/app/__init__.py", line 118, in _display_versions
return model.versions()
File "/home/arpaia/customer-webpage-rewrite/app/models.py", line 37, in versions
return ", ".join([release.version for device in self.devices for release in device.releases])
TypeError: 'Devices' object is not iterable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 1478, in __call__
return self.wsgi_app(environ, start_response)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 1458, in wsgi_app
response = self.handle_exception(e)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 1455, in wsgi_app
response = self.full_dispatch_request()
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 869, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 867, in full_dispatch_request
rv = self.dispatch_request()
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask/app.py", line 852, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/base.py", line 69, in inner
return self._run_view(f, *args, **kwargs)
File "/home/arpaia/customer-webpage-rewrite/.venv/lib/python3.10/site-packages/flask_admin/base.py", line 371, in _run_view
return fn(cls=self, **kwargs)
TypeError: BaseModelView.index_view() got an unexpected keyword argument 'cls'
有人可以帮助我吗?
我希望在前端看到一个空字段,或者至少没有,因为我的用户模型中包含以下代码:
def versions(self):
if self.devices:
return ", ".join([release.version for device in self.devices for release in device.releases])
else:
return ""
你的数据库模型应该有
__str__
方法,Flask-Admin 将使用它在 UI 中显示模型实例。
您的格式化程序应该是常规方法实例,而不是静态方法。例如
def _display_roles(view, context, model, name):
return ", ".join([role.name.capitalize() for role in model.roles])
这里,
view
参数是视图实例,通常写为self
。