【问题标题】:Flask-Admin sort on one to many counted fieldFlask-Admin 对一对多计数字段进行排序
【发布时间】:2016-03-24 15:26:10
【问题描述】:

假设我们在所有者和屏幕之间存在一对多关系(所有者可以拥有多个屏幕)。

可以通过使用 hybrid_property 并在关系上调用 count() 来显示一个显示每个所有者拥有的屏幕数量的列。 但是我还没有找到一种方法来使这个值计算值在 Web 界面中可排序: 如果我在 column_sortable_list 中添加 number_of_screens,则会出现以下错误:

Traceback (most recent call last):
  File "app.py", line 71, in <module>
    admin.add_view(OwnerAdmin(Owner, db.session))
  File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 319, in __init__
    menu_icon_value=menu_icon_value)
  File "C:\Python27\lib\site-packages\flask_admin\model\base.py", line 718, in __init__
    self._refresh_cache()
  File "C:\Python27\lib\site-packages\flask_admin\model\base.py", line 795, in _refresh_cache
    self._sortable_columns = self.get_sortable_columns()
  File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 539, in get_sortable_columns
    column, path = self._get_field_with_path(c)
  File "C:\Python27\lib\site-packages\flask_admin\contrib\sqla\view.py", line 365, in _get_field_with_path
    value = getattr(model, attribute)
  File "C:\Python27\lib\site-packages\sqlalchemy\ext\hybrid.py", line 740, in __get__
    return self.expr(owner)
  File "app.py", line 48, in number_of_screens
    return self.screens.count()
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\attributes.py", line 193, in __getattr__
    key)
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Owner.screens has an attribute 'count'

这里有一个示例代码来说明这个问题:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.hybrid import hybrid_property

import flask_admin as admin
from flask_admin.contrib import sqla
from flask_admin.contrib.sqla.filters import IntGreaterFilter

# Create application
app = Flask(__name__)

# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'

# Create in-memory database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample_db_2.sqlite'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)


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


class Screen(db.Model):
    __tablename__ = 'screen'
    id = db.Column(db.Integer, primary_key=True)
    width = db.Column(db.Integer, nullable=False)
    height = db.Column(db.Integer, nullable=False)
    owner_id = db.Column(db.Integer, db.ForeignKey('owner.id'))
    owner = db.relationship('Owner',
        backref=db.backref('screens', lazy='dynamic'))

    @hybrid_property
    def number_of_pixels(self):
        return self.width * self.height


class Owner(db.Model):
    __tablename__ = 'owner'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode)

    @hybrid_property
    def number_of_screens(self):
        return self.screens.count()


class ScreenAdmin(sqla.ModelView):
    ''' Flask-admin can not automatically find a hybrid_property yet. You will
        need to manually define the column in list_view/filters/sorting/etc.'''
    list_columns = ['id', 'width', 'height', 'number_of_pixels']
    column_sortable_list = ['id', 'width', 'height', 'number_of_pixels']

    # make sure the type of your filter matches your hybrid_property
    column_filters = [IntGreaterFilter(Screen.number_of_pixels,
                                       'Number of Pixels')]

class OwnerAdmin(sqla.ModelView):
    ''' Flask-admin can not automatically find a hybrid_property yet. You will
        need to manually define the column in list_view/filters/sorting/etc.'''
    list_columns = ['id', 'name', 'number_of_screens']
    column_sortable_list = ['id', 'name', 'number_of_screens']


# Create admin
admin = admin.Admin(app, name='Example: SQLAlchemy2', template_mode='bootstrap3')
admin.add_view(ScreenAdmin(Screen, db.session))
admin.add_view(OwnerAdmin(Owner, db.session))

if __name__ == '__main__':

    # Create DB
    db.create_all()

    # Start app
    app.run(debug=True)

【问题讨论】:

    标签: python flask flask-sqlalchemy flask-admin


    【解决方案1】:

    您需要为您的 number_of_screens 混合属性添加一个 hybrid property expression 修饰符。这是一种发出计算特定所有者的屏幕计数所需的 SQL 的方法。例如

    class Owner(db.Model):
        __tablename__ = 'owner'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.Unicode)
    
        @hybrid_property
        def number_of_screens(self):
            return len(self.screens)
    
        @number_of_screens.expression
        def number_of_screens(cls):
            return db.select([db.func.count(Screen.id)]).where(Screen.owner_id == cls.id).label("number_of_screens") 
    

    【讨论】:

    • 谢谢,这正是我想要的!
    • TypeError: count() takes exactly one argument (0 given) :(
    • 让它在@hybrid_property下与return len(self.screens)一起工作。
    猜你喜欢
    • 2020-11-12
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 2012-03-04
    相关资源
    最近更新 更多