主模型的创建/编辑视图中的辅助表属性

时间:2017-03-20 18:35:37

标签: forms python-3.x flask many-to-many flask-admin

我希望能够在创建或编辑任一主表期间创建和编辑多对多关系的辅助表属性(关系表)。因此,当我编辑其中一个主表并将关系添加到另一个模型(隐式使用辅助表)时,我希望能够访问/编辑该辅助关系的属性。

更具体地说:

模型

# "Primary" table
class Paper(db.Model):
    __tablename__ = 'papers'
    ...
    chapters = db.relationship(Chapter, secondary="chapter_paper")
    ...

# "Primary" table
class Chapter(db.Model):
    ...
    papers = db.relationship('Paper', secondary="chapter_paper")
    ...

# "Secondary" table
class ChapterPaper(db.Model):
    __tablename__ = 'chapter_paper'
    paper_id = db.Column(db.Integer,
                         db.ForeignKey('papers.id'),
                         primary_key=True)
    chapter_id = db.Column(db.Integer,
                           db.ForeignKey('chapters.id'),
                           primary_key=True)

    ### WANT TO EDIT
    printed = db.Column(db.Boolean, default=False)
    note = db.Column(db.Text, nullable=True)
    ### WANT TO EDIT


    paper = db.relationship('Paper',
                            backref=db.backref("chapter_paper_assoc",
                                               lazy='joined'),
                            lazy='joined')
    chapter = db.relationship(Chapter,
                              backref=db.backref("chapter_paper_assoc",
                                                 lazy='joined'),
                              lazy='joined')

因此,对于这个例子,我希望能够编辑"打印"和"注意" ChapterPaper的属性来自Paper的创建/编辑形式和烧瓶管理中的章节。

ModelViews

# MainModelView subclasses flask_admin.contrib.sqla.ModelView
class PaperModelView(MainModelView):
    ...
    form_columns = (
        'title',
        'abstract',
        'doi',
        'pubmed_id',
        'link',
        'journals',
        'keywords',
        'authors',
        'chapters',
    )
    # Using form_columns allows CRUD for the many to many
    # relation itself, but does not allow access to secondary attributes
    ...

所以,老实说我对如何做到这一点一无所知。如果我将表单字段添加为附加项,然后手动验证它们? (我不知道该怎么做)

即便如此,在表单中添加额外字段并不能真正涵盖多个模型。任何人都可以告诉我如何做到这一点,或者指向一个教程/甚至是一些随机项目中代码的相关示例?

谢谢!

1 个答案:

答案 0 :(得分:1)

好的,这是很多工作,需要大量的RTFM,但是一旦我开始,它就非常简单。

在没有简洁API的情况下执行此操作的方法是扩展模型视图并使用您自己的表单替换创建/编辑表单。

这是我的表单类:

class ExtendedPaperForm(FlaskForm):
    title = StringField()
    abstract = TextAreaField()
    doi = StringField()
    pubmed_id = StringField()
    link = StringField()
    journals = QuerySelectMultipleField(
        query_factory=_get_model(Journal),
        allow_blank=False,
    )
    issue = StringField()
    volume = StringField()
    pages = StringField()
    authors = QuerySelectMultipleField(
        query_factory=_get_model(Author),
        allow_blank=False,
    )
    keywords = QuerySelectMultipleField(
        query_factory=_get_model(Keyword),
        allow_blank=True,
    )
    chapters_printed = QuerySelectMultipleField(
        query_factory=_get_model(Chapter),
        allow_blank=True,
        label="Chapters (Printed)",
    )
    chapters = QuerySelectMultipleField(
        query_factory=_get_model(Chapter),
        allow_blank=True,
        label="Chapters (All)",
    )

实现此功能的重要部分是on_model_change方法,该方法在保存模型之前执行操作。

...
    def on_model_change(self, form, model, is_created):
        """
        Perform some actions before a model is created or updated.
        Called from create_model and update_model in the same transaction (if it has any meaning for a store backend).
        By default does nothing.

        Parameters:
        form – Form used to create/update model
        model – Model that will be created/updated
        is_created – Will be set to True if model was created and to False if edited
        """

        all_chapters = list(set(form.chapters.data + form.chapters_printed.data))
        for chapter in all_chapters:

            if chapter in form.chapters_printed.data:  # if chapter in both, printed takes priority
                chapter_paper = ChapterPaper.query.filter_by(chapter_id=chapter.id, paper_id=model.id).first()

                if not chapter_paper:
                    chapter_paper = ChapterPaper(chapter_id=chapter.id, paper_id=model.id)

                chapter_paper.printed = True
                db.session.add(chapter_paper)

        journal = None
        if form.journals.data:
            journal = form.journals.data[0]

        if journal:  # Assumes only 1 journal if there are any journals in this field
            issue = form.issue.data
            volume = form.volume.data
            pages = form.pages.data
            journal_paper = JournalPaper.query.filter_by(journal_id=journal.id, paper_id=model.id).first()

            if not journal_paper:
                journal_paper = JournalPaper(journal_id=journal.id, paper_id=model.id)

            journal_paper.issue = issue
            journal_paper.volume = volume
            journal_paper.pages = pages
            db.session.add(journal_paper)
...