【问题标题】:Problem Using QuerySelectMultipleField in Flask-Admin Edit Form在 Flask-Admin 编辑表单中使用 QuerySelectMultipleField 的问题
【发布时间】:2021-11-20 22:58:12
【问题描述】:

我是新来的 Flask-Admin,我正在尝试自定义一个模型编辑表单。我的用户模型有一个roles 属性,它是一个relationship 通过role_users 表到Role.name,具有外键约束。

到目前为止一切正常,除了 Flask-Admin 默认用户编辑表单将roles 呈现为字符串字段。我想将其呈现为 QuerySelectMultipleField。我的模型是:

from flask import current_app
from flask_login import UserMixin
from werkzeug.security import check_password_hash, generate_password_hash

from app import db, login_manager

roles = db.Table(
    'role_users',
    db.Column('user_id', db.String(64), db.ForeignKey('users.username')),
    db.Column('role_id', db.String(80), db.ForeignKey('roles.name')),
    db.PrimaryKeyConstraint('user_id', 'role_id')
)


class User(UserMixin, db.Model):
    __tablename__ = 'users'
    username = db.Column(db.String(64), primary_key=True)
    password_hash = db.Column(db.String(128))
    firstname = db.Column(db.String(64))
    lastname = db.Column(db.String(64))
    roles = db.relationship('Role', secondary=roles, backref=db.backref('users', lazy='dynamic'))
    
    def __init__(self, username='', password=''):
        default = Role.query.filter_by(name='View_contact').one()
        self.roles.append(default)
        self.username = username
        self.password = password
        
    def __repr__(self):
        return '<User {}>'.format(self.username)
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)
    
    def check_password(self, password):
        return check_password_hash(self.password_hash, password)
    
    @property
    def password(self):
        raise AttributeError('Password is not a readable attribute.')
    
    @password.setter
    def password(self, password):
        self.set_password(password)
    
    # Required for user_loader with non-integer User PK not named 'id'
    def get_id(self):
        return self.username
    
    def grant_role(self, role_name):
        if not self.has_role(role_name):
            role = Role.query.filter_by(name=role_name).first()
            self.roles.append(role)
            db.session.commit()
    
    def revoke_role(self, role_name):
        if self.has_role(role_name):
            role = Role.query.filter_by(name=role_name).first()
            self.roles.remove(role)
    
    def has_role(self, role_name):
        for role in self.roles:
            if role.name == role_name:
                return True
        
        return False


@login_manager.user_loader
def load_user(username):
    return User.query.get(str(username))
    

class Role(db.Model):
    __tablename__ = 'roles'
    role_list = [
        'View_contact',
        'Add_contact',
        'Edit_contact',
        'Delete_contact',
        'View_user',
        'Add_user',
        'Edit_user',
        'Delete_user',
        'Admin',
    ]
    name = db.Column(db.String(80), primary_key=True)
    description = db.Column(db.String(255))
    
    def __init__(self, name):
        self.name = name
    
    def __repr__(self):
        return '<Role {}>'.format(self.name)
    
    @classmethod
    def load_roles(cls):
        for role_name in cls.role_list:
            role = Role.query.filter_by(name=role_name).first()
            if role is None:
                role = Role(name=role_name)
                db.session.add(role)
        
        db.session.commit()

我的自定义 ModelView 是

from flask_admin.contrib.sqla import ModelView
from flask_admin.contrib.sqla.fields import QuerySelectMultipleField, QuerySelectField
from flask_admin.form import Select2TagsWidget, Select2Widget
from wtforms import PasswordField

from app import db
from app.models import Role, User

class UserView(ModelView):
    column_display_pk = True
    form_extra_fields = {
        'password': PasswordField('Password'),
        'role_sel': QuerySelectMultipleField(
            label='Roles',
            query_factory=lambda: Role.query.all,
            widget=Select2TagsWidget()
        )
    }
    
    form_columns = (
        'username',
        'password',
        'firstname',
        'lastname',
        'role_sel',
    )
    
    def on_model_change(self, form, User, is_created):
        if form.password.data != '':
            User.set_password(form.password.data)

但是当我尝试访问 admin/user/new/admin/user/edit 时,Flask 会抛出 AttributeError: 'QuerySelectMultipleField' object has no attribute '_value'

如果我尝试从 QuerySelectMultipleField &amp; Select2TagsWidget 更改为 QuerySelectField &amp; Select2Widget Flask throws TypeError: 'method' object is not iterable

谁能告诉我我做错了什么? _value() 是我需要实现的方法吗?我正在使用 Flask-Admin 1.5.8。

【问题讨论】:

    标签: python flask flask-admin


    【解决方案1】:

    你的错误是你的次要需要指向的地方

    记录关系的role_users 表

    lass User(UserMixin, db.Model):
        __tablename__ = 'users'
        username = db.Column(db.String(64), primary_key=True)
        password_hash = db.Column(db.String(128))
        firstname = db.Column(db.String(64))
        lastname = db.Column(db.String(64))
        roles = db.relationship('Role', secondary=role_users)
    
    
    

    【讨论】:

      【解决方案2】:

      首先,我认为你应该为你的表使用另一个命名系统:

      # change this to roles_users
      roles = db.Table(
          'role_users',
          db.Column('user_id', db.String(64), db.ForeignKey('users.username')),
          db.Column('role_id', db.String(80), db.ForeignKey('roles.name')),
          db.PrimaryKeyConstraint('user_id', 'role_id')
      )
      

      现在,转向一个可能的解决方案:

      即使您认为您的UserRolemodels 上不需要id 属性(或列),默认使用id 作为模型的主键。如果您只遵循这些模式,许多扩展和框架将“按原样”工作。

      我认为您的实现出了什么问题,是 FlaskAdmin 的 ModelView 在定义 Roles 模型主键时遇到问题。

      试试下面的代码:

      class Role(db.Model):
          ...
          id = db.Column(db.Integer, primary_key=True)
          name = db.Column(db.String(80), unique=True)
          ...
      
      
      roles = db.Table(
          'role_users',
          db.Column('user_id', db.String(64), db.ForeignKey('users.username')),
          db.Column('role_id', db.String(80), db.ForeignKey('roles.id')),
          db.PrimaryKeyConstraint('user_id', 'role_id')
      )
      

      并从form_args 中删除role_sel,并在UserView 中删除form_columns

      FlaskAdmin 应该猜测User.roles 是一个多对多字段并将其呈现为多选字段。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-06
        • 2020-02-12
        • 1970-01-01
        • 1970-01-01
        • 2016-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多