Flask-Admin:更改 inline_models 的排序顺序?

Flask-Admin: Change sort order of inline_models?

在Flask-Admin中,有没有办法控制inline_models生成的列表的顺序?它似乎是按数据库顺序出现的,即按 ID 主键排序。

也就是说,如果我有一个 Author has_many Books,而我的 AuthorModelView class 有 inline_models = (Books,),那么书籍总是按book_id。将 column_default_sort 传递给内联模型,尝试按(比如)标题或 date_purchased 排序,但没有任何效果。有什么办法可以解决这个问题吗?

指定关系时指定 order_by 参数,see docs。如果您想在运行时按特定字段排序,请参阅末尾的注释。

作者 -> 书籍的模型声明示例。在这里,我们对书名字段进行升序排序 - order_by='Book.title.asc()' :

class Author(db.Model):
    __tablename__ = 'authors'

    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.Text(length=255), nullable=False)
    last_name = db.Column(db.Text(length=255), nullable=False)
    books = db.relationship("Book", order_by='Book.title.asc()', cascade="all,delete-orphan", backref=db.backref('author'))

    def __str__(self):
        return f"ID: {self.id}; First Name: {self.first_name}; Last Name: {self.last_name}"


class Book(db.Model):

    __tablename__ = 'books'

    id = db.Column(db.Integer, primary_key=True)
    author_id = db.Column(db.Integer, db.ForeignKey('authors.id'), nullable=False, index=True)
    title = db.Column(db.Text(length=255), nullable=False)

    def __str__(self):
        return f"ID: {self.id}; Title: {self.title}; Author ID: {self.author_id}"

单个文件完整示例:

from faker import Faker
import click
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib import sqla

db = SQLAlchemy()


class Author(db.Model):
    __tablename__ = 'authors'

    id = db.Column(db.Integer, primary_key=True)
    first_name = db.Column(db.Text(length=255), nullable=False)
    last_name = db.Column(db.Text(length=255), nullable=False)
    books = db.relationship("Book", order_by='Book.title.asc()', cascade="all,delete-orphan", backref=db.backref('author'))

    def __str__(self):
        return f"ID: {self.id}; First Name: {self.first_name}; Last Name: {self.last_name}"


class Book(db.Model):

    __tablename__ = 'books'

    id = db.Column(db.Integer, primary_key=True)
    author_id = db.Column(db.Integer, db.ForeignKey('authors.id'), nullable=False, index=True)
    title = db.Column(db.Text(length=255), nullable=False)

    def __str__(self):
        return f"ID: {self.id}; Title: {self.title}; Author ID: {self.author_id}"


app = Flask(__name__)

app.config['SECRET_KEY'] = '123456790'
app.config['SQLALCHEMY_ECHO'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample.sqlite'

db.init_app(app)


@app.cli.command('create-database', short_help='Create Authors database')
@click.option('--count', default=100, help='Number of authors (default 100)')
def create_database(count):

    """
        Create database with "count" authors
    """

    db.drop_all()
    db.create_all()
    _faker = Faker()
    for _ in range(0, count):
        _author = Author(
            first_name=_faker.first_name(),
            last_name=_faker.last_name(),
        )
        db.session.add(_author)
        for _ in range(0, _faker.pyint(1, 20)):
            _book = Book(
                title=_faker.sentence(),
                author=_author
            )
            db.session.add(_book)

    db.session.commit()


class AuthorView(sqla.ModelView):

    # default sort: last_name ascending
    column_default_sort = ('last_name', False)

    inline_models = (Book,)


# Flask views
@app.route('/')
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'


admin = Admin(app, template_mode="bootstrap3")
admin.add_view(AuthorView(Author, db.session))


if __name__ == '__main__':
    app.run()

运行下面的命令初始化一个SQLite DB。

flask create-database --count 100

如果您想在运行时更改排序字段,请覆盖视图的 get_one() 方法并使用 Python 直接对检测列表进行排序。例如,按 ISBN 字段而不是标题排序:

class Author2View(sqla.ModelView):
    def get_one(self, id):
        _author = super().get_one(id)
        _author.books = sorted(_author.books, key=lambda book: book.isbn)
        return _author

    # default sort: last_name ascending
    column_default_sort = ('last_name', False)

    inline_models = (Book,)

admin.add_view(Author2View(Author, db.session, name="Author 2", endpoint='author-2'))