首页 > 解决方案 > Flask-Admin 中的简单多对多问题

问题描述

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

我有一个Quotation表,一个Subject表,一个QuotationSubject表,它必须是一个实际的类而不是一个关联表,但我不关心关联表中为此目的的额外列;它们是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,例如

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

同样,在我create看来,我不是从 QuotationSubject 表中获得一个包含十几个主题的列表,而是获得一个包含所有内容的巨大列表。

我看过一些inline_models东西,这里的一些帖子建议,这也没有奏效,但无论如何还有其他帖子(例如,具有多对多关系模型的 Flask-Admin 视图)表明我我做的应该工作。如果有人能指出我做错了什么,我将不胜感激。

标签: pythonflaskflask-sqlalchemyflask-admin

解决方案


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

Flask 中多对多关系最经典的例子是用户角色。以下是用户 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 表的关系。这真的是你想要的吗?您说您不需要访问 Flask-Admin 的 QuotationSubject 表中的额外列。但是你在其他地方需要它们吗?调用quotationsubject实际返回QuotationSubject. 我相信这就是 Flask-Admin 在创建视图中列出所有 QuotationSubject 行的原因。

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

如果您想在其他地方访问关联模型(并且如果由于某种原因它真的不能成为关联代理),那么在每个明确指向它的模型类中创建第二个关系。然后,您可能需要在 Flask-Admin 中使用form_excluded_columns和排除该关系column_exclude_list


推荐阅读