Flask-Admin 中的简单多对多问题

Simple Many-to-Many issue in Flask-Admin

我正在将 Flask-Admin 添加到现有的 Flask 应用程序中(使用 Python 3,以及 MySQL 和 SQLAlchemy),我根本不知道如何获得多对-许多关系才能正确呈现。我在这里阅读了很多关于此的问题,看起来我正在遵循正确的做法。

我有一个Quotationtable、一个Subjecttable和一个QuotationSubjecttable,必须是实际的class 而不是关联 table,但我不关心关联 table 中用于此目的的额外列;它们是 last_modified 之类的东西,我不需要显示或编辑。这些关系似乎适用于应用程序的其余部分。

去掉这里无关紧要的字段和定义,我有:

class Quotation(db.Model):
    __tablename__ = 'quotation'
    id = db.Column(db.Integer, primary_key=True)
    word = db.Column(db.String(50))
    description = db.Column(db.Text)
    created = db.Column(db.TIMESTAMP, default=db.func.now())
    last_modified = db.Column(db.DateTime, server_default=db.func.now())

    subject = db.relationship("QuotationSubject", back_populates="quotation")

    def __str__(self):
        return self.word

class Subject(db.Model):
    __tablename__ = 'subject'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    created = db.Column(db.TIMESTAMP, default=db.func.now())
    last_modified = db.Column(db.DateTime, server_default=db.func.now())

    quotation = db.relationship("QuotationSubject", back_populates="subject")

    def __str__(self):
        return self.name

class QuotationSubject(db.Model):
    __tablename__ = 'quotation_subject'
    id = db.Column(db.Integer, primary_key=True)
    quotation_id = db.Column(db.Integer, db.ForeignKey('quotation.id'), default=0, nullable=False)
    subject_id = db.Column(db.Integer, db.ForeignKey('subject.id'), default=0, nullable=False)
    created = db.Column(db.TIMESTAMP, default=db.func.now())
    last_modified = db.Column(db.DateTime, server_default=db.func.now())

    quotation = db.relationship('Quotation', back_populates='subject', lazy='joined')
    subject = db.relationship('Subject', back_populates='quotation', lazy='joined')

在我的 admin.py 中,我只有:

class QuotationModelView(ModelView):
    column_searchable_list = ['word', 'description']

    form_excluded_columns = ['created', 'last_modified']
    column_list = ('word', 'subject')


admin.add_view(QuotationModelView(Quotation, db.session))

就是这样。

在我的 list 视图中,我没有看到 subject 值,而是在 QuotationSubject table 中看到相关条目,例如

test    <QuotationSubject 1>, <QuotationSubject 17>, <QuotationSubject 18>
book    <QuotationSubject 2>

同样,在我的 create 视图中,我没有从 QuotationSubject table 中得到一个包含十几个主题的列表,而是一个包含所有内容的巨大列表。

我看了一些 inline_models 的东西,这里的一些帖子建议,这也没有用,但无论如何还有其他帖子(例如 )建议我正在做的事情应该有效。如果有人能指出我做错了什么,我将不胜感激。

首先,我担心您的问题中缺少某些内容,因为我没有看到 Citation class 的定义。但这似乎不是问题。

Flask 中多对多关系的最 classic 示例是用户的角色。以下是用户 M2M 关系的工作角色:

class RolesUsers(Base):
    __tablename__ = 'roles_users'

    id = db.Column(db.Integer(), primary_key=True)
    user_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
    role_id = db.Column(db.Integer(), db.ForeignKey('role.id'))

class Role(RoleMixin, Base):
    __tablename__ = 'role'

    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(80), unique=True)

    def __repr__(self):
        return self.name

class User(UserMixin, Base):
    __tablename__ = 'user'

    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), index=True, unique=True)

    roles = db.relationship('Role', secondary='roles_users',
                            backref=db.backref('users', lazy='dynamic'))

在 Flask-Admin 中:

from user.models import User, Role

admin.add_view(PVCUserView(User, db.session))
admin.add_view(PVCModelView(Role, db.session))

请注意,关系只声明一次,backref 所以它是双向的。看起来你正在为此使用 back_populates.

对于您所描述的情况,看起来您的代码直接声明了与 M2M table 的关系。这真的是你想要的吗?您说您不需要访问 Flask-Admin 的 QuotationSubject table 中的额外列。但是你在其他地方需要它们吗?调用 quotationsubject 实际上 return QuotationSubject 的实例对我来说似乎很奇怪。我相信这就是 Flask-Admin 在创建视图中列出所有 QuotationSubject 行的原因。

所以我的建议是尝试将您的关系设置为直接指向目标模型 class,同时将 M2M table 作为 secondary

如果您想在其他地方访问关联模型(如果由于某种原因它确实不能成为 Association Proxy),则在每个模型中创建第二个关系 class 明确指向它。然后,您可能需要使用 form_excluded_columnscolumn_exclude_list.

在 Flask-Admin 中排除该关系